1 /*
2  * gnote
3  *
4  * Copyright (C) 2010-2013,2016-2017,2019-2021 Aurimas Cernius
5  * Copyright (C) 2009 Hubert Figuiere
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 #include <libxml/xmlmemory.h>
23 #include <libxml/xpathInternals.h>
24 #include <libxslt/extensions.h>
25 
26 #include <glibmm/i18n.h>
27 
28 #include "config.h"
29 #include "sharp/exception.hpp"
30 #include "sharp/files.hpp"
31 #include "sharp/streamwriter.hpp"
32 #include "sharp/string.hpp"
33 #include "sharp/uri.hpp"
34 #include "sharp/xsltargumentlist.hpp"
35 #include "debug.hpp"
36 #include "iactionmanager.hpp"
37 #include "ignote.hpp"
38 #include "preferences.hpp"
39 #include "notewindow.hpp"
40 #include "utils.hpp"
41 
42 #include "exporttohtmlnoteaddin.hpp"
43 #include "exporttohtmldialog.hpp"
44 #include "notenameresolver.hpp"
45 
46 #define STYLESHEET_NAME "exporttohtml.xsl"
47 
48 
49 using gnote::Preferences;
50 
51 namespace exporttohtml {
52 
53 
ExportToHtmlModule()54 ExportToHtmlModule::ExportToHtmlModule()
55 {
56   ADD_INTERFACE_IMPL(ExportToHtmlNoteAddin);
57 }
58 
59 sharp::XslTransform *ExportToHtmlNoteAddin::s_xsl = NULL;
60 
61 
initialize()62 void ExportToHtmlNoteAddin::initialize()
63 {
64 
65 }
66 
67 
shutdown()68 void ExportToHtmlNoteAddin::shutdown()
69 {
70 }
71 
72 
on_note_opened()73 void ExportToHtmlNoteAddin::on_note_opened()
74 {
75   register_main_window_action_callback("exporttohtml-export",
76     sigc::mem_fun(*this, &ExportToHtmlNoteAddin::export_button_clicked));
77 }
78 
79 
get_actions_popover_widgets() const80 std::vector<gnote::PopoverWidget> ExportToHtmlNoteAddin::get_actions_popover_widgets() const
81 {
82   auto widgets = NoteAddin::get_actions_popover_widgets();
83   auto button = gnote::utils::create_popover_button("win.exporttohtml-export", _("Export to HTML…"));
84   widgets.push_back(gnote::PopoverWidget::create_for_note(gnote::EXPORT_TO_HTML_ORDER, button));
85   return widgets;
86 }
87 
88 
export_button_clicked(const Glib::VariantBase &)89 void ExportToHtmlNoteAddin::export_button_clicked(const Glib::VariantBase&)
90 {
91   ExportToHtmlDialog dialog(ignote(), get_note()->get_title() + ".html");
92   int response = dialog.run();
93   Glib::ustring output_path = dialog.get_filename();
94 
95   if (response != Gtk::RESPONSE_OK) {
96     return;
97   }
98 
99   DBG_OUT("Exporting Note '%s' to '%s'...", get_note()->get_title().c_str(),
100           output_path.c_str());
101 
102   sharp::StreamWriter writer;
103   Glib::ustring error_message;
104 
105   try {
106     // FIXME: Warn about file existing.  Allow overwrite.
107     sharp::file_delete(output_path);
108 
109     writer.init(output_path);
110     write_html_for_note (writer, get_note(), dialog.get_export_linked(),
111                          dialog.get_export_linked_all());
112 
113     // Save the dialog preferences now that the note has
114     // successfully been exported
115     dialog.save_preferences ();
116 
117     try {
118       sharp::Uri output_uri(output_path);
119       gnote::utils::open_url(*get_host_window(), "file://" + output_uri.get_absolute_uri());
120     }
121     catch (const Glib::Exception & ex) {
122       ERR_OUT(_("Could not open exported note in a web browser: %s"),
123                ex.what().c_str());
124 
125       Glib::ustring detail = Glib::ustring::compose(
126                                  // TRANSLATORS: %1%: boost format placeholder for the path
127                                  _("Your note was exported to \"%1\"."),
128                                  output_path);
129 
130       // Let the user know the note was saved successfully
131       // even though showing the note in a web browser failed.
132       gnote::utils::HIGMessageDialog msg_dialog(
133         get_host_window(),
134         GTK_DIALOG_DESTROY_WITH_PARENT,
135         Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK,
136         _("Note exported successfully"),
137         detail);
138       msg_dialog.run();
139     }
140   }
141 #if 0
142   catch (UnauthorizedAccessException) {
143     error_message = Catalog.GetString ("Access denied.");
144   }
145   catch (DirectoryNotFoundException) {
146     error_message = Catalog.GetString ("Folder does not exist.");
147   }
148 #endif
149   catch (const sharp::Exception & e) {
150     ERR_OUT(_("Could not export: %s"), e.what());
151 
152     error_message = e.what();
153   }
154   writer.close ();
155 
156   if (!error_message.empty())
157   {
158     ERR_OUT(_("Could not export: %s"), error_message.c_str());
159 
160     Glib::ustring msg = Glib::ustring::compose(
161                             _("Could not save the file \"%1\""),
162                             output_path.c_str());
163 
164     gnote::utils::HIGMessageDialog msg_dialog(&dialog,
165                                               GTK_DIALOG_DESTROY_WITH_PARENT,
166                                               Gtk::MESSAGE_ERROR,
167                                               Gtk::BUTTONS_OK,
168                                               msg, error_message);
169     msg_dialog.run();
170   }
171 }
172 
173 
174 
to_lower(xmlXPathParserContextPtr ctxt,int)175 static void to_lower(xmlXPathParserContextPtr ctxt,
176                      int)
177 {
178   const xmlChar *input = xmlXPathPopString(ctxt);
179   gchar * lower = g_utf8_strdown((const gchar*)input, -1);
180   xmlXPathReturnString(ctxt, xmlStrdup((const xmlChar*)lower));
181   g_free(lower);
182 }
183 
184 
get_note_xsl()185 sharp::XslTransform & ExportToHtmlNoteAddin::get_note_xsl()
186 {
187   if(s_xsl == NULL) {
188     int result = xsltRegisterExtModuleFunction((const xmlChar *)"ToLower",
189                                                (const xmlChar *)"http://beatniksoftware.com/tomboy",
190                                                &to_lower);
191     DBG_OUT("xsltRegisterExtModule %d", result);
192     if(result == -1) {
193       DBG_OUT("xsltRegisterExtModule failed");
194     }
195 
196     s_xsl = new sharp::XslTransform;
197     Glib::ustring stylesheet_file = DATADIR "/gnote/" STYLESHEET_NAME;
198 
199     if (sharp::file_exists (stylesheet_file)) {
200       DBG_OUT("ExportToHTML: Using user-custom %s file.", STYLESHEET_NAME);
201       s_xsl->load(stylesheet_file);
202     }
203 #if 0
204     else {
205       Stream resource = asm.GetManifestResourceStream (stylesheet_name);
206       if (resource != null) {
207         XmlTextReader reader = new XmlTextReader (resource);
208         s_xsl->load (reader, null, null);
209         resource.Close ();
210       }
211       else {
212         DBG_OUT("Unable to find HTML export template '%s'.", STYLESHEET_NAME);
213       }
214     }
215 #endif
216 
217   }
218   return *s_xsl;
219 }
220 
221 
222 
223 
write_html_for_note(sharp::StreamWriter & writer,const gnote::Note::Ptr & note,bool export_linked,bool export_linked_all)224 void ExportToHtmlNoteAddin::write_html_for_note (sharp::StreamWriter & writer,
225                                                  const gnote::Note::Ptr & note,
226                                                  bool export_linked,
227                                                  bool export_linked_all)
228 {
229   Glib::ustring s_writer;
230   s_writer = note->manager().note_archiver().write_string(note->data());
231   xmlDocPtr doc = xmlParseMemory(s_writer.c_str(), s_writer.bytes());
232 
233   sharp::XsltArgumentList args;
234   args.add_param ("export-linked", "", export_linked);
235   args.add_param ("export-linked-all", "", export_linked_all);
236   args.add_param ("root-note", "", gnote::utils::XmlEncoder::encode(note->get_title()));
237 
238   if(ignote().preferences().enable_custom_font()) {
239     Glib::ustring font_face = ignote().preferences().custom_font_face();
240     Pango::FontDescription font_desc (font_face);
241     Glib::ustring font = Glib::ustring::compose("font-family:'%1';", font_desc.get_family());
242 
243     args.add_param ("font", "", font);
244   }
245 
246   NoteNameResolver resolver(note->manager(), note);
247   get_note_xsl().transform(doc, args, writer, resolver);
248 
249   xmlFreeDoc(doc);
250 }
251 
252 
253 }
254 
255