[glom/feature_backup2] Added Developer/Restore Backup menu item.



commit 644a18a6f6d32104c6135d564e53cb3a64cf07b0
Author: Murray Cumming <murrayc murrayc com>
Date:   Thu Jul 22 15:23:29 2010 +0200

    Added Developer/Restore Backup menu item.
    
    * glom/application.[h.cc]: Added a Developer/Restore Backup menu item,
      which lets the user choose a .tar.gz, untars it in /tmp/ and opens it.
    
    * tests/test_selfhosting_new_empty.cc:
    * glom/libglom/utils.[h|cc]: Move delete_directory() to Utils.
      Added get_directory_child_with_suffix(), used to find the .glom file inside
      an untarred directory.

 ChangeLog                                          |   12 ++
 glom/application.cc                                |  112 +++++++++++++++++++-
 glom/application.h                                 |    1 +
 glom/libglom/connectionpool_backends/postgres.cc   |    2 +-
 glom/libglom/utils.cc                              |   97 +++++++++++++++--
 glom/libglom/utils.h                               |   15 +++
 glom/main.cc                                       |    1 +
 .../mode_design/translation/window_translations.cc |    2 +
 tests/test_selfhosting_new_empty.cc                |   48 +--------
 9 files changed, 228 insertions(+), 62 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index bc2b7c5..626032c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2010-07-22  Murray Cumming  <murrayc murrayc com>
 
+  Added Developer/Restore Backup menu item.
+
+	* glom/application.[h.cc]: Added a Developer/Restore Backup menu item,
+  which lets the user choose a .tar.gz, untars it in /tmp/ and opens it.
+
+	* tests/test_selfhosting_new_empty.cc:
+	* glom/libglom/utils.[h|cc]: Move delete_directory() to Utils.
+  Added get_directory_child_with_suffix(), used to find the .glom file inside
+  an untarred directory.
+
+2010-07-22  Murray Cumming  <murrayc murrayc com>
+
 	Saving from a backup: Now works.
 
 	* glom/application.[h|cc]: on_document_load(): Simplify the code to
diff --git a/glom/application.cc b/glom/application.cc
index 5e2c90d..5e52aa8 100644
--- a/glom/application.cc
+++ b/glom/application.cc
@@ -563,6 +563,10 @@ void Application::init_menus()
   m_listDeveloperActions.push_back(action);
   m_refActionGroup_Others->add(action, sigc::mem_fun(*this, &Application::on_menu_developer_export_backup));
 
+  action = Gtk::Action::create("GlomAction_Menu_Developer_RestoreBackup", _("_Restore Backup"));
+  m_listDeveloperActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*this, &Application::on_menu_developer_restore_backup));
+
   m_action_show_layout_toolbar = Gtk::ToggleAction::create("GlomAction_Menu_Developer_ShowLayoutToolbar", _("_Show Layout Toolbar"));
   m_listDeveloperActions.push_back(m_action_show_layout_toolbar);
   m_refActionGroup_Others->add(m_action_show_layout_toolbar, sigc::mem_fun(*this, &Application::on_menu_developer_show_layout_toolbar));
@@ -619,8 +623,10 @@ void Application::init_menus()
     "          <menuitem action='GlomAction_Menu_Developer_ActivePlatform_Normal' />"
     "          <menuitem action='GlomAction_Menu_Developer_ActivePlatform_Maemo' />"
     "        </menu>"
-    "        <menuitem action='GlomAction_Menu_Developer_ExportBackup' />"
     "        <menuitem action='GlomAction_Menu_Developer_ShowLayoutToolbar' />"
+    "        <separator />"
+    "        <menuitem action='GlomAction_Menu_Developer_ExportBackup' />"
+    "        <menuitem action='GlomAction_Menu_Developer_RestoreBackup' />"
     "      </menu>"
 #endif // !GLOM_ENABLE_CLIENT_ONLY
     "    </placeholder>"
@@ -1969,7 +1975,7 @@ bool Application::recreate_database_from_backup(const Glib::ustring& backup_uri,
   }
 
   //Restore the database from the backup:
-  std::cout << "DEBUG: original_dir_path=" << original_dir_path << std::endl;
+  //std::cout << "DEBUG: original_dir_path=" << original_dir_path << std::endl;
   const bool restored = connection_pool->convert_backup(
     sigc::mem_fun(*this, &Application::on_connection_convert_backup_progress), original_dir_path);
 
@@ -2807,7 +2813,7 @@ void Application::on_menu_developer_export_backup()
 
       if(saved)
       {
-
+        std::cerr << G_STRFUNC << "tar failed with command:" << command_tar << std::endl;
       }
 
       if(m_dialog_progess_save_backup)
@@ -2822,6 +2828,106 @@ void Application::on_menu_developer_export_backup()
     ui_warning(_("Export Backup failed."), _("There was an error while exporting the backup."));
 }
 
+void Application::on_menu_developer_restore_backup()
+{
+  Gtk::FileChooserDialog file_dlg(_("Choose a backup file"), Gtk::FILE_CHOOSER_ACTION_OPEN);
+  file_dlg.set_transient_for(*this);
+  file_dlg.set_local_only(); //Because we can't untar remote files.
+
+  Gtk::FileFilter filter;
+  filter.set_name(_(".tar.gz Backup files"));
+  filter.add_pattern("*.tar.gz");
+  filter.add_pattern("*.tgz");
+  file_dlg.add_filter(filter);
+
+  file_dlg.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+  file_dlg.add_button(_("Restore"), Gtk::RESPONSE_OK);
+
+  const int result = file_dlg.run();
+  file_dlg.hide();
+  if(result != Gtk::RESPONSE_OK)
+    return;
+
+  // We cannot use an uri here, because we cannot untar remote files.
+  const std::string filename_tarball = file_dlg.get_filename();
+  if(filename_tarball.empty())
+    return;
+
+  const std::string path_tar = Glib::find_program_in_path("tar");
+  if(path_tar.empty())
+  {
+    std::cerr << G_STRFUNC << ": The tar executable could not be found." << std::endl;
+    return;
+  }
+
+  //Create a temporary directory into which we will untar the tarball:
+  std::string path_tmp = Glib::build_filename(
+    Glib::get_tmp_dir(), Glib::path_get_basename(filename_tarball));
+  path_tmp += "_extracted";
+
+  //Make sure that the directory does not exist already:
+  const Glib::ustring uri_tmp = Glib::filename_to_uri(path_tmp);
+  if(!Utils::delete_directory(uri_tmp))
+  {
+    std::cerr << G_STRFUNC << "Error from Utils::delete_directory() while trying to remove directory: " << uri_tmp << std::endl;
+    return;
+  }
+
+  //Create the tmp directory:
+  const int mkdir_succeeded = g_mkdir_with_parents(path_tmp.c_str(), 0770);
+  if(mkdir_succeeded == -1)
+  {
+    std::cerr << G_STRFUNC << "Error from g_mkdir_with_parents() while trying to create directory: " << path_tmp << std::endl;
+    perror("Error from g_mkdir_with_parents");
+
+    return;
+  }
+
+  //Untar into the tmp directory:
+  //TODO: Find some way to do this without using the command-line,
+  //which feels fragile:
+  const std::string command_tar = "\"" + path_tar + "\"" +
+    " --force-local --no-wildcards" + //Avoid side-effects of special characters.
+    " -xzf"
+    " \"" + filename_tarball + "\"" +
+    " --directory \"" + path_tmp + "\"";
+
+  //std::cout << "DEBUG: command_tar=" << command_tar << std::endl;
+
+  const bool untarred = Glom::Spawn::execute_command_line_and_wait(command_tar,
+    sigc::mem_fun(*this, &Application::on_connection_convert_backup_progress));
+  if(!untarred)
+  {
+    std::cerr << G_STRFUNC << ": tar failed with command:" << command_tar << std::endl;
+  }
+
+  if(m_dialog_progess_convert_backup)
+  {
+    delete m_dialog_progess_convert_backup;
+    m_dialog_progess_convert_backup = 0;
+  }
+
+  if(!untarred)
+    ui_warning(_("Restore Backup failed."), _("There was an error while restoring the backup. The tar utility failed to extract the archive."));
+
+  //Open the .glom file that is in the tmp directory:
+  const Glib::ustring untarred_uri = Utils::get_directory_child_with_suffix(uri_tmp, ".glom", true /* recurse */);
+  if(untarred_uri.empty())
+  {
+    ui_warning(_("Restore Backup failed."), _("There was an error while restoring the backup. The .glom file could not be found."));
+    return;
+  }
+
+  //std::cout << "DEBUG: untarred_uri=" << untarred_uri << std::endl;
+  open_document(untarred_uri);
+
+  //Delete the temporary untarred directory:
+  //Actually, we just leave this here, where the system will clean it up anyway,
+  //because open_document() starts a new process,
+  //so we don't know when we can safely delete the files.
+  //Utils::delete_directory(uri_tmp);
+}
+
 void Application::on_menu_developer_show_layout_toolbar()
 {
   m_pFrame->show_layout_toolbar(m_action_show_layout_toolbar->get_active());
diff --git a/glom/application.h b/glom/application.h
index 12104ee..0978b0b 100644
--- a/glom/application.h
+++ b/glom/application.h
@@ -172,6 +172,7 @@ private:
   void on_menu_developer_active_platform_normal();
   void on_menu_developer_active_platform_maemo();
   void on_menu_developer_export_backup();
+  void on_menu_developer_restore_backup();
   void on_menu_developer_show_layout_toolbar();
 
   void on_window_translations_hide();
diff --git a/glom/libglom/connectionpool_backends/postgres.cc b/glom/libglom/connectionpool_backends/postgres.cc
index 91db725..58cc023 100644
--- a/glom/libglom/connectionpool_backends/postgres.cc
+++ b/glom/libglom/connectionpool_backends/postgres.cc
@@ -542,7 +542,7 @@ bool Postgres::save_password_to_pgpass(const Glib::ustring username, const Glib:
   //Move any existing file out of the way:
   if(file_exists_filepath(filepath_pgpass))
   {
-    std::cout << "DEBUG: File exists: " << filepath_pgpass << std::endl;
+    //std::cout << "DEBUG: File exists: " << filepath_pgpass << std::endl;
     filepath_previous = filepath_pgpass + ".glombackup";
     if(g_rename(filepath_pgpass.c_str(), filepath_previous.c_str()) != 0)
     {
diff --git a/glom/libglom/utils.cc b/glom/libglom/utils.cc
index 86c7af7..059d6df 100644
--- a/glom/libglom/utils.cc
+++ b/glom/libglom/utils.cc
@@ -236,7 +236,7 @@ static void add_to_relationships_list(type_list_relationships& list_relationship
     list_relationships.push_back(uses_rel);
   }
 
- 
+
 }
 
 
@@ -307,7 +307,7 @@ Glib::ustring Utils::build_sql_select_fields_to_get(const Glib::ustring& table_n
     return sql_part_fields;
   }
 
-  //LEFT OUTER JOIN will get the field values from the other tables, 
+  //LEFT OUTER JOIN will get the field values from the other tables,
   //and give us our fields for this table even if there is no corresponding value in the other table.
   for(type_list_relationships::const_iterator iter = list_relationships.begin(); iter != list_relationships.end(); ++iter)
   {
@@ -340,7 +340,7 @@ Glib::ustring Utils::build_sql_select_with_where_clause(const Glib::ustring& tab
     table_name, fieldsToGet, sort_clause, sql_part_from, sql_part_leftouterjoin);
 
   //Build the whole SQL statement:
-  Glib::ustring result = 
+  Glib::ustring result =
     "SELECT " + sql_part_fields +
     " FROM \"" + table_name + '\"';
 
@@ -488,7 +488,7 @@ Utils::type_list_values_with_second Utils::get_choice_values(const sharedptr<con
         itempair.second = datamodel->get_value_at(1, row, error);
 #endif
 
-      list_values.push_back(itempair);      
+      list_values.push_back(itempair);
     }
   }
   else
@@ -521,7 +521,7 @@ Glib::ustring Utils::string_escape_underscores(const Glib::ustring& text)
   return result;
 }
 
-/** Get just the first part of a locale, such as de_DE, 
+/** Get just the first part of a locale, such as de_DE,
  * ignoring, for instance, .UTF-8 or \ euro at the end.
  */
 Glib::ustring Utils::locale_simplify(const Glib::ustring& locale_id)
@@ -647,13 +647,13 @@ Utils::type_vec_strings Utils::string_separate(const Glib::ustring& str, const G
   const Glib::ustring::size_type size_separator = separator.size();
 
   //A stack of quotes, so that we can handle nested quotes, whether they are " or ':
-  typedef std::stack<Glib::ustring> type_queue_quotes; 
+  typedef std::stack<Glib::ustring> type_queue_quotes;
   type_queue_quotes m_current_quotes;
 
   Glib::ustring::size_type unprocessed_start = 0;
   Glib::ustring::size_type item_start = 0;
   while(unprocessed_start < size)
-  { 
+  {
     //std::cout << "while unprocessed: un_processed_start=" << unprocessed_start << std::endl;
     Glib::ustring::size_type posComma = str.find(separator, unprocessed_start);
 
@@ -666,18 +666,18 @@ Utils::type_vec_strings Utils::string_separate(const Glib::ustring& str, const G
       if(ignore_quoted_separator)
       {
         //std::cout << "  debug: attempting to ignore quoted separators: " << separator << std::endl;
-       
+
         Glib::ustring::size_type posLastQuote = unprocessed_start;
 
         //std::cout << "    debug: posLastQuote=" << posLastQuote << std::endl;
         //std::cout << "    debug: posComma=" << posComma << std::endl;
- 
-  
+
+
         bool bContinue = true;
         while(bContinue && (posLastQuote < posComma))
         {
           //std::cout << "  continue" << std::endl;
-          Glib::ustring closing_quote; 
+          Glib::ustring closing_quote;
           if(!m_current_quotes.empty())
             closing_quote = m_current_quotes.top();
 
@@ -752,7 +752,7 @@ Utils::type_vec_strings Utils::string_separate(const Glib::ustring& str, const G
         // Do not add this item to the result, because it was quoted.
         continue;
       }
-     
+
       unprocessed_start = posComma + size_separator; //The while loops stops when this is empty.
     }
     else //if no separator found:
@@ -841,5 +841,78 @@ bool Utils::file_exists(const Glib::ustring& uri)
   }
 }
 
+bool Utils::delete_directory(const Glib::RefPtr<Gio::File>& directory)
+{
+  if(!(directory->query_exists()))
+    return true;
+
+  //(Recursively) Delete any child files and directories,
+  //so we can delete this directory.
+  Glib::RefPtr<Gio::FileEnumerator> enumerator = directory->enumerate_children();
+
+  Glib::RefPtr<Gio::FileInfo> info = enumerator->next_file();
+  while(info)
+  {
+    Glib::RefPtr<Gio::File> child = directory->get_child(info->get_name());
+    bool removed_child = false;
+    if(child->query_file_type() == Gio::FILE_TYPE_DIRECTORY)
+      removed_child = delete_directory(child);
+    else
+      removed_child = child->remove();
+
+    if(!removed_child)
+       return false;
+
+    info = enumerator->next_file();
+  }
+
+  //Delete the actual directory:
+  if(!directory->remove())
+    return false;
+
+  return true;
+}
+
+bool Utils::delete_directory(const std::string& uri)
+{
+  Glib::RefPtr<Gio::File> file = Gio::File::create_for_uri(uri);
+  return delete_directory(file);
+}
+
+/** For instance, to find the first file in the directory with a .glom extension.
+ */
+Glib::ustring Utils::get_directory_child_with_suffix(const Glib::ustring& uri_directory, const std::string& suffix, bool recursive)
+{
+  Glib::RefPtr<Gio::File> directory = Gio::File::create_for_uri(uri_directory);
+  Glib::RefPtr<Gio::FileEnumerator> enumerator = directory->enumerate_children();
+
+  Glib::RefPtr<Gio::FileInfo> info = enumerator->next_file();
+  while(info)
+  {
+    Glib::RefPtr<const Gio::File> child = directory->get_child(info->get_name());
+
+    const Gio::FileType file_type = child->query_file_type();
+    if(file_type == Gio::FILE_TYPE_REGULAR)
+    {
+      //Check the filename:
+      const std::string basename = child->get_basename();
+      if(string_remove_suffix(basename, suffix) != basename)
+        return child->get_uri();
+    }
+    else if(recursive && file_type == Gio::FILE_TYPE_DIRECTORY)
+    {
+      //Look in sub-directories too:
+      const Glib::ustring result = get_directory_child_with_suffix(child->get_uri(), suffix, recursive);
+      if(!result.empty())
+        return result;
+    }
+
+    info = enumerator->next_file();
+  }
+
+  return Glib::ustring();
+}
+
+
 
 } //namespace Glom
diff --git a/glom/libglom/utils.h b/glom/libglom/utils.h
index f46aade..4ab5b46 100644
--- a/glom/libglom/utils.h
+++ b/glom/libglom/utils.h
@@ -25,6 +25,7 @@
 #include <libglom/data_structure/numeric_format.h>
 
 #include <libglom/data_structure/layout/layoutitem_field.h>
+#include <giomm/file.h>
 
 namespace Glom
 {
@@ -131,6 +132,20 @@ Glib::ustring string_remove_suffix(const Glib::ustring& str, const Glib::ustring
 
 bool file_exists(const Glib::ustring& uri);
 
+/** Delete a directory, if it exists, and its contents.
+ * Unlike g_file_delete(), this does not fail if the directory is not empty.
+ */
+bool delete_directory(const Glib::RefPtr<Gio::File>& directory);
+
+/** Delete a directory, if it exists, and its contents.
+ * Unlike g_file_delete(), this does not fail if the directory is not empty.
+ */
+bool delete_directory(const std::string& uri);
+
+/** For instance, to find the first file in the directory with a .glom extension.
+ */
+Glib::ustring get_directory_child_with_suffix(const Glib::ustring& uri_directory, const std::string& suffix, bool recursive);
+
 } //namespace Utils
 
 } //namespace Glom
diff --git a/glom/main.cc b/glom/main.cc
index 4535539..9ceb9b0 100644
--- a/glom/main.cc
+++ b/glom/main.cc
@@ -546,6 +546,7 @@ main(int argc, char* argv[])
       if(!file->query_exists())
       {
         std::cerr << _("Glom: The file does not exist.") << std::endl;
+        std::cerr << "uri: " << input_uri << std::endl;
 
         std::cerr << std::endl << context.get_help() << std::endl;
         return -1;
diff --git a/glom/mode_design/translation/window_translations.cc b/glom/mode_design/translation/window_translations.cc
index 277ac26..f8f8cd4 100644
--- a/glom/mode_design/translation/window_translations.cc
+++ b/glom/mode_design/translation/window_translations.cc
@@ -495,6 +495,7 @@ void Window_Translations::on_button_export()
   
   //Show the file-chooser dialog, to select an output .po file:
   Gtk::FileChooserDialog file_dlg(_("Choose .po File Name"), Gtk::FILE_CHOOSER_ACTION_SAVE);
+  file_dlg.set_transient_for(*this);
   file_dlg.set_do_overwrite_confirmation();
   
   // Only po files
@@ -573,6 +574,7 @@ void Window_Translations::on_button_import()
     return;
 
   Gtk::FileChooserDialog file_dlg(_("Choose .po File Name"), Gtk::FILE_CHOOSER_ACTION_OPEN);
+  file_dlg.set_transient_for(*this);
 
   // Only po files
   Gtk::FileFilter filter;
diff --git a/tests/test_selfhosting_new_empty.cc b/tests/test_selfhosting_new_empty.cc
index f36cc81..f5a1e8f 100644
--- a/tests/test_selfhosting_new_empty.cc
+++ b/tests/test_selfhosting_new_empty.cc
@@ -40,50 +40,6 @@ static void on_cleanup_progress()
   std::cout << "Database cleanup progress" << std::endl;
 }
 
-/** Delete a directory, if it exists, and its contents.
- * Unlike g_file_delete(), this does not fail if the directory is not empty.
- */
-static bool delete_directory(const Glib::RefPtr<Gio::File>& directory)
-{
-  if(!(directory->query_exists()))
-    return true;
-
-  //(Recursively) Delete any child files and directories,
-  //so we can delete this directory.
-  Glib::RefPtr<Gio::FileEnumerator> enumerator = directory->enumerate_children();
-
-  Glib::RefPtr<Gio::FileInfo> info = enumerator->next_file();
-  while(info)
-  {
-    Glib::RefPtr<Gio::File> child = directory->get_child(info->get_name());
-    bool removed_child = false;
-    if(child->query_file_type() == Gio::FILE_TYPE_DIRECTORY)
-      removed_child = delete_directory(child);
-    else
-      removed_child = child->remove();
-
-    if(!removed_child)
-       return false;
-
-    info = enumerator->next_file();
-  }
-
-  //Delete the actual directory:
-  if(!directory->remove())
-    return false;
-
-  return true;
-}
-
-/** Delete a directory, if it exists, and its contents.
- * Unlike g_file_delete(), this does not fail if the directory is not empty.
- */
-static bool delete_directory(const std::string& uri)
-{
-  Glib::RefPtr<Gio::File> file = Gio::File::create_for_uri(uri);
-  return delete_directory(file);
-}
-
 int main()
 {
   Glom::libglom_init();
@@ -139,7 +95,7 @@ int main()
   //Make sure that the file does not exist yet:
   {
     const Glib::ustring uri = Glib::filename_to_uri(temp_filepath_dir);
-    delete_directory(uri);
+    Glom::Utils::delete_directory(uri);
   }
 
   //Save the example as a real file:
@@ -177,7 +133,7 @@ int main()
   //Make sure the directory is removed at the end,
   {
     const Glib::ustring uri = Glib::filename_to_uri(temp_filepath_dir);
-    delete_directory(uri);
+    Glom::Utils::delete_directory(uri);
   }
 
   Glom::libglom_deinit();



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