[tomboy] Fix for Bug 356871 - Allows mass export of HTML, v2



commit 405cc4e74824235128e2770cbcabd6ba48e1ae00
Author: Robert Nordan <rpvn robpvn net>
Date:   Thu May 12 21:50:20 2011 +0200

    Fix for Bug 356871 - Allows mass export of HTML, v2

 Tomboy/Addins/ExportToHtml/ExportToHtml.addin.xml  |    6 +-
 .../ExportToHtml/ExportToHtmlApplicationAddin.cs   |   54 +++
 Tomboy/Addins/ExportToHtml/Makefile.am             |    3 +-
 Tomboy/ExportAllApplicationAddin.cs                |  469 ++++++++++++++++++++
 Tomboy/Makefile.am                                 |    1 +
 5 files changed, 531 insertions(+), 2 deletions(-)
---
diff --git a/Tomboy/Addins/ExportToHtml/ExportToHtml.addin.xml b/Tomboy/Addins/ExportToHtml/ExportToHtml.addin.xml
index 4d28cd1..00776cf 100644
--- a/Tomboy/Addins/ExportToHtml/ExportToHtml.addin.xml
+++ b/Tomboy/Addins/ExportToHtml/ExportToHtml.addin.xml
@@ -5,7 +5,7 @@
 	description="Exports individual notes to HTML."
 	category="Tools"
 	defaultEnabled="true"
-	version="0.1">
+	version="0.2">
 
 	<Runtime>
 		<Import assembly="ExportToHtml.dll" />
@@ -15,6 +15,10 @@
 		<Addin id="Tomboy" version="0.10" />
 	</Dependencies>
 
+	<Extension path="/Tomboy/ApplicationAddins">
+		<ApplicationAddin type="Tomboy.ExportToHtml.ExportToHtmlApplicationAddin" />
+	</Extension>
+
 	<Extension path="/Tomboy/NoteAddins">
 		<NoteAddin type="Tomboy.ExportToHtml.ExportToHtmlNoteAddin" />
 	</Extension>
diff --git a/Tomboy/Addins/ExportToHtml/ExportToHtmlApplicationAddin.cs b/Tomboy/Addins/ExportToHtml/ExportToHtmlApplicationAddin.cs
new file mode 100644
index 0000000..737d4bc
--- /dev/null
+++ b/Tomboy/Addins/ExportToHtml/ExportToHtmlApplicationAddin.cs
@@ -0,0 +1,54 @@
+using System;
+using System.IO;
+using Tomboy;
+using Mono.Unix;
+
+namespace Tomboy.ExportToHtml
+{
+	/// <summary>
+	/// Extends the ExportAll add-in to export to HTML.
+	/// </summary>
+	public class ExportToHtmlApplicationAddin : ExportAllApplicationAddin
+	{
+
+		ExportToHtmlNoteAddin exporter = new ExportToHtmlNoteAddin ();
+
+		/// <summary>
+		/// Sets the names of the export type.
+		/// </summary>
+		protected override void SetNames ()
+		{
+			export_file_suffix = "html";
+			export_type_pretty_name = Catalog.GetString ("HTML");
+		}
+
+		/// <summary>
+		/// Exports a single Note to HTML in a specified location.
+		/// </summary>
+		public override void ExportSingleNote (Note note,
+		                                string output_folder)
+		{
+			string output_path = output_folder + SanitizeNoteTitle (note.Title)
+				+ "." + export_file_suffix;
+
+			Logger.Debug ("Exporting Note '{0}' to '{1}'...", note.Title, output_path);
+
+			StreamWriter writer = null;
+
+			try {
+				// FIXME: Warn about file existing.  Allow overwrite.
+				File.Delete (output_path);
+			} catch {
+			}
+
+			writer = new StreamWriter (output_path);
+
+			exporter.WriteHTMLForNote (writer, note, false, false);
+
+			if (writer != null)
+				writer.Close ();
+
+			return;
+		}
+	}
+}
diff --git a/Tomboy/Addins/ExportToHtml/Makefile.am b/Tomboy/Addins/ExportToHtml/Makefile.am
index 4d65027..78dc548 100644
--- a/Tomboy/Addins/ExportToHtml/Makefile.am
+++ b/Tomboy/Addins/ExportToHtml/Makefile.am
@@ -15,7 +15,8 @@ ADDIN_NAME = ExportToHtml
 TARGET = $(top_builddir)/bin/addins/$(ADDIN_NAME).dll
 CSFILES = \
 	$(srcdir)/ExportToHtmlDialog.cs		\
-	$(srcdir)/ExportToHtmlNoteAddin.cs
+	$(srcdir)/ExportToHtmlNoteAddin.cs		\
+	$(srcdir)/ExportToHtmlApplicationAddin.cs
 RESOURCES = \
 	-resource:$(srcdir)/$(ADDIN_NAME).addin.xml \
 	-resource:$(srcdir)/ExportToHtml.xsl
diff --git a/Tomboy/ExportAllApplicationAddin.cs b/Tomboy/ExportAllApplicationAddin.cs
new file mode 100644
index 0000000..a42eb47
--- /dev/null
+++ b/Tomboy/ExportAllApplicationAddin.cs
@@ -0,0 +1,469 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Mono.Unix;
+using Tomboy;
+using Tomboy.Notebooks;
+
+namespace Tomboy
+{
+	/// <summary>
+	/// An abstract class which handles all the details of "export all",
+	/// to be subclassed with a method that defines what to do with a
+	/// single note.
+	/// </summary>
+	public abstract class ExportAllApplicationAddin : ApplicationAddin
+	{
+
+		/// <summary>
+		/// The filename suffix of the export type, e.g. "html" or "txt".
+		/// Don't put a punctuation mark in front!
+		/// </summary>
+		protected string export_file_suffix;
+
+		/// <summary>
+		/// The full name to be used in the menu. Can include spaces, should use Catalog.GetString.
+		/// </summary>
+		protected string export_type_pretty_name;
+
+		private Gtk.ActionGroup action_group;
+		private uint action_group_id;
+		private ActionManager am = Tomboy.ActionManager;
+		private bool initialized = false;
+
+		/// <summary>
+		/// Called when Tomboy has started up and is nearly 100% initialized.
+		/// </summary>
+		public override void Initialize ()
+		{
+			// Gets names from subclass.
+			SetNames ();
+
+			/*Adds "Export All Notes/Notebook To ***" to Tomboy's Main Menu */
+
+			if (am.FindActionByName ("NoteExportAll"+export_file_suffix+"Action") == null) {
+
+				am.MainWindowActions.Add (new Gtk.ActionEntry [] {
+					new Gtk.ActionEntry ("NoteExportAll"+export_file_suffix+"Action", null,
+					Catalog.GetString ("Export All Notes to "+export_type_pretty_name), null,
+					Catalog.GetString ("Start exporting notes to "+export_type_pretty_name), null)
+				});
+				am.MainWindowActions.Add (new Gtk.ActionEntry [] {
+					new Gtk.ActionEntry ("NoteExportNotebook"+export_file_suffix+"Action", null,
+					Catalog.GetString ("Export selected notebook to "+export_type_pretty_name), null,
+					Catalog.GetString ("Start exporting notebook to "+export_type_pretty_name), null)
+				});
+
+				action_group = new Gtk.ActionGroup ("Export");
+				action_group.Add (new Gtk.ActionEntry [] {
+					new Gtk.ActionEntry ("ToolsMenuAction", null,
+						Catalog.GetString ("_Tools"), null, null, null),
+
+					new Gtk.ActionEntry ("ExportMenuAction", Gtk.Stock.New,
+					Catalog.GetString ("Export"), null,
+					Catalog.GetString ("Export your notes."), null),
+
+					new Gtk.ActionEntry ("ExportAllNotes"+export_file_suffix+"Action", null,
+						Catalog.GetString ("Export All Notes To "+export_type_pretty_name), null, null,
+						delegate {
+							am ["NoteExportAll"+export_file_suffix+"Action"].Activate ();
+					}),
+					new Gtk.ActionEntry ("ExportNotebook"+export_file_suffix+"Action", null,
+						Catalog.GetString ("Export Selected Notebook To "+export_type_pretty_name), null, null,
+						delegate {
+							am ["NoteExportNotebook"+export_file_suffix+"Action"].Activate ();
+					})
+				});
+
+				action_group_id = am.UI.AddUiFromString (String.Format (@"
+				                <ui>
+				                <menubar name='MainWindowMenubar'>
+				                <placeholder name='MainWindowMenuPlaceholder'>
+				                <menu name='ToolsMenu' action='ToolsMenuAction'>
+				                <menu name='ExportMenu' action='ExportMenuAction'>
+				                <menuitem name='ExportAllNotes{0}' action='ExportAllNotes{0}Action' />
+				                <menuitem name='ExportNotebook{0}' action='ExportNotebook{0}Action' />
+				                </menu>
+				                </menu>
+				                </placeholder>
+				                </menubar>
+				                </ui>
+				                ", export_file_suffix)
+				                 );
+
+				am.UI.InsertActionGroup (action_group, 0);
+
+				am ["NoteExportAll"+export_file_suffix+"Action"].Activated += ExportAllButtonClicked;
+				am ["NoteExportNotebook"+export_file_suffix+"Action"].Activated += ExportNotebookButtonClicked;
+
+				initialized = true;
+			}
+		}
+
+		/// <summary>
+		/// Must be overridden in order to set names for internal menu use
+		/// and file naming (export_file_suffix) and what the user sees
+		/// (export_type_pretty_name).
+		/// </summary>
+		protected abstract void SetNames ();
+
+		/// <summary>
+		/// Called just before Tomboy shuts down for good.
+		/// </summary>
+		public override void Shutdown ()
+		{
+			// Disconnect the event handlers and global menu entries so
+			// there aren't any memory leaks.
+
+			if (action_group != null) {
+				am ["NoteExportAll"+export_file_suffix+"Action"].Activated -= ExportAllButtonClicked;
+				am ["NoteExportNotebook"+export_file_suffix+"Action"].Activated -= ExportAllButtonClicked;
+				am.UI.RemoveUi (action_group_id);
+				am.UI.RemoveActionGroup (action_group);
+				am.MainWindowActions.Remove
+				    (Tomboy.ActionManager.FindActionByName ("NoteExportAll"+export_file_suffix+"Action"));
+				am.MainWindowActions.Remove
+				    (Tomboy.ActionManager.FindActionByName ("NoteExportNotebook"+export_file_suffix+"Action"));
+
+				action_group = null;
+			}
+		}
+
+		void ExportAllButtonClicked (object sender, EventArgs args)
+		{
+			ExportAllNotes ();
+		}
+
+		/// <summary>
+		/// Called when the user chooses "Export All"
+		/// </summary>
+		/// <param name="sender">
+		void ExportAllNotes ()
+		{
+			Logger.Info ("Activated export all to " + export_type_pretty_name);
+
+			//Opens the folder selection dialog
+			ExportMultipleDialog dialog =
+			    new ExportMultipleDialog ("All Notes " + export_type_pretty_name +" Export", export_type_pretty_name);
+			int response = dialog.Run ();
+			if (response != (int) Gtk.ResponseType.Ok) {
+				Logger.Debug("User clicked cancel.");
+				dialog.Destroy ();
+				return;
+			}
+			string output_folder = SanitizePath (dialog.Filename);
+
+			try {
+				Logger.Debug ("Creating an export folder in: " + output_folder);
+				System.IO.Directory.CreateDirectory (output_folder);
+
+				//Iterate through notebooks
+				Notebooks.Notebook notebook;
+				string notebook_folder;
+
+				foreach (Tag tag in TagManager.AllTags) {
+					// Skip over tags that aren't notebooks
+					notebook = NotebookManager.GetNotebookFromTag (tag);
+					if (notebook == null)
+						continue;
+
+					Logger.Debug ("Exporting notebook " + notebook.Name);
+					notebook_folder = SanitizePath (output_folder + System.IO.Path.DirectorySeparatorChar
+					                  + notebook.NormalizedName);
+					System.IO.Directory.CreateDirectory (notebook_folder);
+					ExportNotesInList (notebook.Tag.Notes, notebook_folder);
+
+				}
+
+				//Finally we have to export all unfiled notes.
+				Logger.Debug ("Exporting Unfiled Notes");
+				ExportNotesInList (ListUnfiledNotes (), output_folder);
+
+				//Successful export: clean up and inform.
+				dialog.SavePreferences ();
+				ShowSuccessDialog (output_folder);
+
+			} catch (UnauthorizedAccessException) {
+				Logger.Error (Catalog.GetString ("Could not export, access denied."));
+				ShowErrorDialog (output_folder, dialog,
+				                 Catalog.GetString ("Access denied."));
+				return;
+			} catch (DirectoryNotFoundException) {
+				Logger.Error (Catalog.GetString ("Could not export, folder does not exist."));
+				ShowErrorDialog (output_folder, dialog,
+				                 Catalog.GetString ("Folder does not exist."));
+				return;
+			} catch (Exception ex) {
+				Logger.Error (Catalog.GetString ("Could not export: {0}"), ex);
+				ShowErrorDialog (output_folder, dialog,
+				                 Catalog.GetString ("Unknown error."));
+				return;
+			} finally {
+				if (dialog != null) {
+					dialog.Destroy ();
+					dialog = null;
+				}
+			}
+		}
+
+		/// <summary>
+		/// Called when the user chooses "Export Notebook"
+		/// (Even when "All Notes or "Unfiled Notes" are
+		/// selected.)
+		/// </summary>
+		void ExportNotebookButtonClicked (object sender, EventArgs args)
+		{
+			string output_folder = null;
+			ExportMultipleDialog dialog = null;
+			Logger.Info ("Activated export notebook to " + export_file_suffix);
+
+			Notebook notebook = NoteRecentChanges.GetInstance (Tomboy.DefaultNoteManager).GetSelectedNotebook ();
+
+			try {
+				//Handling the two special notebooks
+				string notebook_name = notebook.NormalizedName;
+				if (notebook_name == "___NotebookManager___AllNotes__Notebook___") {
+					Logger.Info ("This notebook includes all notes, activating Export All");
+					ExportAllNotes ();
+					return;
+				} else if (notebook_name == "___NotebookManager___UnfiledNotes__Notebook___") {
+					dialog = new ExportMultipleDialog (Catalog.GetString ("Unfiled Notes"), export_type_pretty_name);
+					int response = dialog.Run ();
+					output_folder = SanitizePath (dialog.Filename);
+					if (response != (int) Gtk.ResponseType.Ok) {
+						Logger.Debug("User clicked cancel.");
+						dialog.Destroy ();
+						return;
+					}
+
+					Logger.Debug ("Creating an export folder in: " + output_folder);
+					System.IO.Directory.CreateDirectory (output_folder);
+					ExportNotesInList (ListUnfiledNotes (), output_folder);
+				} else {
+					//Ordinary notebooks
+					dialog = new ExportMultipleDialog (notebook_name, export_type_pretty_name);
+					int response = dialog.Run ();
+					output_folder = SanitizePath (dialog.Filename);
+					if (response != (int) Gtk.ResponseType.Ok) {
+						Logger.Debug("User clicked cancel.");
+						dialog.Destroy ();
+						return;
+					}
+
+					Logger.Debug ("Creating an export folder in: " + output_folder);
+					System.IO.Directory.CreateDirectory (output_folder);
+					ExportNotesInList (notebook.Tag.Notes, output_folder);
+				}
+
+				//Successful export: clean up and inform.
+				dialog.SavePreferences ();
+				ShowSuccessDialog (output_folder);
+
+			} catch (UnauthorizedAccessException) {
+				Logger.Error (Catalog.GetString ("Could not export, access denied."));
+				ShowErrorDialog (output_folder, dialog,
+				                 Catalog.GetString ("Access denied."));
+				return;
+			} catch (DirectoryNotFoundException) {
+				Logger.Error (Catalog.GetString ("Could not export, folder does not exist."));
+				ShowErrorDialog (output_folder, dialog,
+				                 Catalog.GetString ("Folder does not exist."));
+				return;
+			} catch (Exception ex) {
+				Logger.Error (Catalog.GetString ("Could not export: {0}"), ex);
+				ShowErrorDialog (output_folder, dialog,
+				                 Catalog.GetString ("Unknown error."));
+				return;
+			} finally {
+				if (dialog != null) {
+					dialog.Destroy ();
+					dialog = null;
+				}
+			}
+		}
+
+		/// <summary>
+		/// Exports the specified list of notes to *** files in the given folder,
+		/// excludes template notes.
+		/// </summary>
+		public void ExportNotesInList (List<Note> note_list, string output_folder)
+		{
+			output_folder = output_folder + System.IO.Path.DirectorySeparatorChar;
+			bool save;
+
+			foreach (Note note in note_list) {
+				save = true;
+				//Checks all tags on note to see if it's a template.
+				foreach (Tag tag in note.Tags) {
+					if (tag.Name.StartsWith (Tag.SYSTEM_TAG_PREFIX + TagManager.TemplateNoteSystemTag))
+						save = false;
+				}
+
+				if (save) {
+					ExportSingleNote (note, output_folder);
+				}
+			}
+			return;
+		}
+
+		/// <summary>
+		/// Finds all notes without a notebook tag and returns them in a list.
+		/// </summary>
+		public List<Note> ListUnfiledNotes ()
+		{
+			List<Note> unfiled_notes = new List<Note> ();
+			//Checks all notes
+			foreach (Note note in Tomboy.DefaultNoteManager.Notes) {
+				if (NotebookManager.GetNotebookFromNote (note) == null)
+					unfiled_notes.Add (note);
+			}
+			return unfiled_notes;
+		}
+
+		/// <summary>
+		/// Exports a single Note to the chosen format and saves it in the specified folder.
+		/// To be implemented in a subclass where the subclass implementation takes care
+		/// of conversion and saving.
+		/// <param name="output_folder">
+		/// The folder which the note is to be saved to. For an all notes export the top level
+		/// folder is chosen by the user and sublevel folders are automatically created for
+		/// each notebook and passed to this method.
+		/// </summary>
+		public abstract void ExportSingleNote (Note note, string output_folder);
+
+		/// <summary>
+		/// Removes elements from the note title that might be problematic in a file name.
+		/// </summary>
+		public string SanitizeNoteTitle (string note_title)
+		{
+			note_title = SanitizePath (note_title);
+
+			//Clearing common folder and file chars
+			note_title = note_title.Replace ('/', '_');
+			note_title = note_title.Replace ('\\', '_');
+			note_title = note_title.Replace ('.', '_');
+
+			return note_title;
+		}
+
+		/// <summary>
+		/// Makes sure a path doesn't have any illegal characters.
+		/// </summary>
+		private string SanitizePath (string path)
+		{
+			char[] invalid_path_chars = Path.GetInvalidPathChars ();
+
+			foreach (char x in invalid_path_chars) {
+				path = path.Replace (x, '_');
+			}
+
+			return path;
+		}
+
+		/// <summary>
+		/// Return true if the addin is initialized
+		/// </summary>
+		public override bool Initialized
+		{
+			get
+			{
+				return initialized;
+			}
+		}
+
+		/// <summary>
+		/// Shows a success dialog when export is complete
+		/// </summary>
+		/// <param name="detail"> A string with details of the export folder.</param>
+		private static void ShowSuccessDialog (string output_folder)
+		{
+			string detail = String.Format (
+			                            Catalog.GetString ("Your notes were exported to \"{0}\"."),
+			                            output_folder);
+
+			HIGMessageDialog msg_dialog =
+			        new HIGMessageDialog (
+			        null,
+			        Gtk.DialogFlags.DestroyWithParent,
+			        Gtk.MessageType.Info,
+			        Gtk.ButtonsType.Ok,
+			        Catalog.GetString ("Notes exported successfully"),
+			        detail);
+			msg_dialog.Run ();
+			msg_dialog.Destroy ();
+		}
+
+		/// <summary>
+		/// Shows an error dialog if things go wrong.
+		/// </summary>
+		/// <param name="output_folder">
+		/// A <see cref="System.String"/> with the name of the folder
+		/// that couldn't be exported to.
+		/// </param>
+		/// <param name="dialog">
+		/// The parent <see cref="ExportMultipleDialog"/>.
+		/// </param>
+		/// <param name="error_message">
+		/// A <see cref="System.String"/> with an error description.
+		/// </param>
+		private static void ShowErrorDialog (string output_folder, ExportMultipleDialog dialog,
+		                                     string error_message)
+		{
+			string msg = String.Format (
+			                     Catalog.GetString ("Could not save the files in \"{0}\""),
+			                     output_folder);
+				HIGMessageDialog msg_dialog =
+				        new HIGMessageDialog (
+				        dialog,
+				        Gtk.DialogFlags.DestroyWithParent,
+				        Gtk.MessageType.Error,
+				        Gtk.ButtonsType.Ok,
+				        msg,
+				        error_message);
+				msg_dialog.Run ();
+				msg_dialog.Destroy ();
+			dialog.Destroy ();
+			Logger.Error (error_message);
+		}
+	}
+
+	/// <summary>
+	/// A utility class for choosing where to export to.
+	/// </summary>
+	public class ExportMultipleDialog : Gtk.FileChooserDialog
+	{
+
+		public ExportMultipleDialog (string default_folder, string export_type_name) :
+		    base (Catalog.GetString ("Create destination folder for " +  export_type_name + " Export"),
+		        null, Gtk.FileChooserAction.Save, new object[] {})
+		//Using action Save insted of CreateFolder because of Win32 issue
+		{
+			AddButton (Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
+			AddButton (Gtk.Stock.Save, Gtk.ResponseType.Ok);
+
+			DefaultResponse = Gtk.ResponseType.Ok;
+			DoOverwriteConfirmation = true;
+			LocalOnly = true;
+
+			ShowAll ();
+
+			LoadPreferences (default_folder);
+		}
+
+		//Using the same directory prefs as a single note export.
+		public void SavePreferences ()
+		{
+			string dir = System.IO.Path.GetDirectoryName (Filename);
+			Preferences.Set (Preferences.EXPORTHTML_LAST_DIRECTORY, dir);
+		}
+
+		protected void LoadPreferences (string default_folder)
+		{
+			string last_dir = (string) Preferences.Get (Preferences.EXPORTHTML_LAST_DIRECTORY);
+			if (last_dir == "")
+				last_dir = Environment.GetEnvironmentVariable ("HOME");
+			SetCurrentFolder (last_dir);
+			CurrentName = default_folder;
+		}
+	}
+}
diff --git a/Tomboy/Makefile.am b/Tomboy/Makefile.am
index 48feb25..bdc7fb3 100644
--- a/Tomboy/Makefile.am
+++ b/Tomboy/Makefile.am
@@ -94,6 +94,7 @@ CSFILES = 					\
 	$(srcdir)/AddinPreferenceFactory.cs	\
 	$(srcdir)/ApplicationAddin.cs		\
 	$(srcdir)/Contrast.cs		\
+	$(srcdir)/ExportAllApplicationAddin.cs		\
 	$(srcdir)/Keybinder.cs			\
 	$(srcdir)/Logger.cs			\
 	$(srcdir)/NativeApplication.cs		\



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]