[gnumeric] Save: verify that the target file hasn't changed underneath us.



commit ff67fe99e377b63f2468aa1c8432c33714683a69
Author: Morten Welinder <terra gnome org>
Date:   Sun Mar 25 15:48:21 2018 -0400

    Save: verify that the target file hasn't changed underneath us.

 NEWS                |    1 +
 configure.ac        |    2 +-
 src/gui-file.c      |   79 +++++++++++++++++++++++++++++++++++++++++++++++++-
 src/workbook-view.c |   54 +++++++++++++++++++++++++++++++++--
 4 files changed, 130 insertions(+), 6 deletions(-)
---
diff --git a/NEWS b/NEWS
index b63cb6b..652938b 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,7 @@ Gnumeric 1.12.40
 
 Morten:
        * Add floating-point environment sanity check.  [#794515]
+       * Before saving, verify that files hasn't changed on disk.  [#334024]
 
 --------------------------------------------------------------------------
 Gnumeric 1.12.39
diff --git a/configure.ac b/configure.ac
index 4e3b9b8..b33afd6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -166,7 +166,7 @@ PKG_PROG_PKG_CONFIG(0.18)
 
 dnl *****************************
 libspreadsheet_reqs="
-       libgoffice-${GOFFICE_API_VER}   >= 0.10.38
+       libgoffice-${GOFFICE_API_VER}   >= 0.10.40
        libgsf-1                >= 1.14.33
        libxml-2.0              >= 2.4.12
 "
diff --git a/src/gui-file.c b/src/gui-file.c
index 78b7bc8..7c8d8fb 100644
--- a/src/gui-file.c
+++ b/src/gui-file.c
@@ -742,6 +742,61 @@ gui_file_save_as (WBCGtk *wbcg, WorkbookView *wb_view, GnmFileSaveAsStyle type,
        return success;
 }
 
+static gboolean
+warn_about_overwrite (WBCGtk *wbcg,
+                     GDateTime *modtime,
+                     GDateTime *known_modtime)
+{
+       GtkWidget *dialog;
+       int response;
+       char *shortname, *filename, *longname, *duri, *modtxt;
+       Workbook *wb = wb_control_get_workbook (GNM_WBC (wbcg));
+       const char *uri;
+       GDateTime *modtime_local;
+
+       uri = go_doc_get_uri (GO_DOC (wb));
+       filename = go_filename_from_uri (uri);
+       if (filename) {
+               shortname = g_filename_display_basename (filename);
+       } else {
+               shortname = g_filename_display_basename (uri);
+       }
+
+       duri = g_uri_unescape_string (uri, NULL);
+       longname = duri
+               ? g_filename_display_name (duri)
+               : g_strdup (uri);
+
+       modtime_local = g_date_time_to_local (modtime);
+       modtxt = g_date_time_format (modtime_local, _("%F %T"));
+       g_date_time_unref (modtime_local);
+
+       dialog = gtk_message_dialog_new_with_markup
+               (wbcg_toplevel (wbcg),
+                GTK_DIALOG_DESTROY_WITH_PARENT,
+                GTK_MESSAGE_WARNING,
+                GTK_BUTTONS_NONE,
+                _("The file you are about to save has changed on disk. If you continue, you will overwrite 
someone else's changes.\n\n"
+                  "File: <b>%s</b>\n"
+                  "Location: %s\n\n"
+                  "Last modified: <b>%s</b>\n"),
+                shortname, longname, modtxt);
+       gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+                               _("Overwrite"), GTK_RESPONSE_YES,
+                               _("Cancel"), GTK_RESPONSE_NO,
+                               NULL);
+       g_free (shortname);
+       g_free (longname);
+       g_free (duri);
+       g_free (filename);
+       g_free (modtxt);
+
+       gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_NO);
+       response = go_gtk_dialog_run (GTK_DIALOG (dialog),
+                                     wbcg_toplevel (wbcg));
+       return response == GTK_RESPONSE_YES;
+}
+
 gboolean
 gui_file_save (WBCGtk *wbcg, WorkbookView *wb_view)
 {
@@ -760,17 +815,37 @@ gui_file_save (WBCGtk *wbcg, WorkbookView *wb_view)
                return gui_file_save_as (wbcg, wb_view,
                                         GNM_FILE_SAVE_AS_STYLE_SAVE, NULL);
        else {
-               gboolean ok;
+               gboolean ok = TRUE;
+               const char *uri = go_doc_get_uri (GO_DOC (wb));
+               GDateTime *known_modtime = go_doc_get_modtime (GO_DOC (wb));
+               GDateTime *modtime = go_file_get_modtime (uri);
+               gboolean debug_modtime = gnm_debug_flag ("modtime");
 
                /* We need a ref because a Ctrl-Q at the wrong time will
                   cause the workbook to disappear at the end of the
                   save.  */
                g_object_ref (wb);
 
-               ok = wb_view_save (wb_view, GO_CMD_CONTEXT (wbcg));
+               if (modtime && known_modtime) {
+                       if (g_date_time_equal (known_modtime, modtime)) {
+                               if (debug_modtime)
+                                       g_printerr ("Modtime match\n");
+                       } else {
+                               if (debug_modtime)
+                                       g_printerr ("Modtime mismatch\n");
+                               ok = warn_about_overwrite (wbcg, modtime, known_modtime);
+                       }
+               }
+
+               if (ok)
+                       ok = wb_view_save (wb_view, GO_CMD_CONTEXT (wbcg));
                if (ok)
                        workbook_update_history (wb, GNM_FILE_SAVE_AS_STYLE_SAVE);
                g_object_unref (wb);
+
+               if (modtime)
+                       g_date_time_unref (modtime);
+
                return ok;
        }
 }
diff --git a/src/workbook-view.c b/src/workbook-view.c
index 37838ab..0a71175 100644
--- a/src/workbook-view.c
+++ b/src/workbook-view.c
@@ -1095,6 +1095,31 @@ wb_view_save_to_uri (WorkbookView *wbv, GOFileSaver const *fs,
        }
 }
 
+static GDateTime *
+get_uri_modtime (GsfInput *input, const char *uri)
+{
+       GDateTime *modtime = NULL;
+
+       if (input) {
+               modtime = gsf_input_get_modtime (input);
+               if (modtime)
+                       g_date_time_ref (modtime);
+       }
+
+       if (!modtime)
+               modtime = go_file_get_modtime (uri);
+
+       if (gnm_debug_flag ("modtime")) {
+               char *s = modtime
+                       ? g_date_time_format (modtime, "%F %T")
+                       : g_strdup ("?");
+               g_printerr ("Modtime of %s is %s\n", uri, s);
+               g_free (s);
+       }
+
+       return modtime;
+}
+
 /**
  * wb_view_save_as:
  * @wbv: Workbook View
@@ -1132,12 +1157,20 @@ wb_view_save_as (WorkbookView *wbv, GOFileSaver *fs, char const *uri,
        has_error   = go_io_error_occurred (io_context);
        has_warning = go_io_warning_occurred (io_context);
        if (!has_error) {
-               if (workbook_set_saveinfo
-                   (wb, go_file_saver_get_format_level (fs), fs)) {
+               GOFileFormatLevel fl = go_file_saver_get_format_level (fs);
+               if (workbook_set_saveinfo (wb, fl, fs)) {
                        if (go_doc_set_uri (GO_DOC (wb), uri)) {
+                               GDateTime *modtime;
+
                                go_doc_set_dirty (GO_DOC (wb), FALSE);
                                /* See 634792.  */
                                go_doc_set_pristine (GO_DOC (wb), FALSE);
+
+                               modtime = get_uri_modtime (NULL, uri);
+                               go_doc_set_modtime (GO_DOC (wb), modtime);
+                               if (gnm_debug_flag ("modtime"))
+                                       g_printerr ("Modtime set\n");
+                               g_date_time_unref (modtime);
                        }
                } else
                        workbook_set_last_export_uri (wb, uri);
@@ -1168,12 +1201,14 @@ wb_view_save (WorkbookView *wbv, GOCmdContext *context)
        Workbook        *wb;
        GOFileSaver     *fs;
        gboolean has_error, has_warning;
+       char const *uri;
 
        g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), FALSE);
        g_return_val_if_fail (GO_IS_CMD_CONTEXT (context), FALSE);
 
        wb = wb_view_get_workbook (wbv);
        g_object_ref (wb);
+       uri = go_doc_get_uri (GO_DOC (wb));
 
        fs = workbook_get_file_saver (wb);
        if (fs == NULL)
@@ -1190,8 +1225,14 @@ wb_view_save (WorkbookView *wbv, GOCmdContext *context)
 
        has_error   = go_io_error_occurred (io_context);
        has_warning = go_io_warning_occurred (io_context);
-       if (!has_error)
+       if (!has_error) {
+               GDateTime *modtime = get_uri_modtime (NULL, uri);
+               go_doc_set_modtime (GO_DOC (wb), modtime);
+               if (gnm_debug_flag ("modtime"))
+                       g_printerr ("Modtime set\n");
+               g_date_time_unref (modtime);
                go_doc_set_dirty (GO_DOC (wb), FALSE);
+       }
        if (has_error || has_warning)
                go_io_error_display (io_context);
 
@@ -1267,12 +1308,19 @@ workbook_view_new_from_input (GsfInput *input,
        if (optional_fmt != NULL) {
                Workbook *new_wb;
                gboolean old;
+               GDateTime *modtime;
 
                new_wbv = workbook_view_new (NULL);
                new_wb = wb_view_get_workbook (new_wbv);
                if (optional_uri)
                        go_doc_set_uri (GO_DOC (new_wb), optional_uri);
 
+               // Grab the modtime before we actually do the reading
+               modtime = get_uri_modtime (input, optional_uri);
+               go_doc_set_modtime (GO_DOC (new_wb), modtime);
+               if (modtime)
+                       g_date_time_unref (modtime);
+
                /* disable recursive dirtying while loading */
                old = workbook_enable_recursive_dirty (new_wb, FALSE);
                go_file_opener_open (optional_fmt, optional_enc, io_context,


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