Repository: biolab/orange3

Issue:
Autosave checkbox status in Save Data widget not saved in ows file if reopened
Hi,
I use Orange 3.26.0 on Win 10 and used the standard 64 bit exe installer. (thank you so much for your awesome software and esecially the interactive t-SNE viewer widget!)
I want to automatically safe a csv file every time the Orange is startet via the corresonding *.ows file.
Unfortunately, the status of the checkbox "Autosave when resceiving new data" of the **"Save Data" widget** is not stored/saved in the ows file. If I check the box, safe the *.ows file and reopen the file, the checkbox is unchecked again:
![image](https://user-images.githubusercontent.com/68894423/89411452-b9d92900-d725-11ea-92d6-27602289a766.png)
I used this file:
[Interactive_Visualizations_ows.zip](https://github.com/biolab/orange3/files/5028231/Interactive_Visualizations_ows.zip)
It seems like, this information is not passed to the ows file.




Discussion:
This property is actually saved in .ows but changed in the widget initialization when opening the saved workflow. It happens here: https://github.com/biolab/orange3/blob/6193d329e7e71698b0c19c5d57b64ca5682f1ed4/Orange/widgets/utils/save/owsavebase.py#L130
I think it is made so to prevent overriding existing file when opening the saved workflow. Am I right @janezd?
OK thanks a lot for the information!  
As this seems to be an intended behaviour, I will close this issue.
For the record: I changed line 130 in my owsavebase.py file to get the desired functionality : "#self.auto_save = True"
In the original code it looks like, the "self.auto_save = False" is not set, if the path is relative. Unfortunately I can only set an absolute path (even if the file is saved in the same folder as the ows file, see screenshot from help doc).
![image](https://user-images.githubusercontent.com/68894423/89731525-fec3cf00-da47-11ea-8252-7f5864efc53a.png)
 I don´t know if this is wanted or not, at least it´s not what the help documentation says.
> Unfortunately I can only set an absolute path (even if the file is saved in the same folder as the ows file, see screenshot from help doc).

`self._absolute_path` is always absolute, but `self.stored_path` which is saved to .ows should be relative if the file is placed in a same dir or in a sub-dir. How did you set the absolute path?

I will reopen this issue to wait what @janezd tells about changing an autosave checkbox.
Yes, this is by design. One reason is to prevent overwriting an existing file and the other is to prevent writing a file to another person's disk by sending him an .ows, which  seems like (a very innocent) security hole.
I would still like to keep in mind the users' desire to automate workflows. We should think about possible solutions and adjustments to current widgets to enable this.
Thanks for your comments.
I understand that this security hole is an issue and see why you implemented its prevention.
@PrimozGodec I set the absolute path with the common Windows "Save As" File dialogue. There I can not set a relative path (or maybe I´m too stupid  ^^:) )
Yes @ajdapretnar this is exactly my point. Now I have to set this checkbox every time I open the file, which is a bit unhandy for a more or less automated workflow. An option where User can bypass this security issue consciously would be good.

I'm with @ajdapretnar on preserving automation and portability of workflows.

Regarding the first reason (to prevent overwriting an existing file), this seems to only be a problem if the user is playing around with the workflow, making changes they do not wish to save. If one wishes to keep a definitive version of a saved file, they should use version control.

Regarding the second reason (preventing writing a file), opening and running a workflow to generate files could be the intended outcome. To patch the security hole, maybe we could constrain files to be written only in the directory of the `.ows` and its subdirectories? 
Also, this got me thinking, how should we handle OWPythonScript as a security hole, especially if it implements IPython?

Resolving patch:
diff --git a/Orange/widgets/utils/save/owsavebase.py b/Orange/widgets/utils/save/owsavebase.py
--- a/Orange/widgets/utils/save/owsavebase.py
+++ b/Orange/widgets/utils/save/owsavebase.py
@@ -122,23 +122,21 @@ def _abs_path_from_setting(self):
         """
         Compute absolute path from `stored_path` from settings.
 
-        Auto save is disabled unless stored_path is relative and exists.
+        Absolute stored path is used only if it exists.
+        Auto save is disabled unless stored_path is relative.
         """
         workflow_dir = self.workflowEnv().get("basedir")
         if os.path.isabs(self.stored_path):
-            path = self.stored_path
-            self.auto_save = False
+            if os.path.exists(self.stored_path):
+                self.auto_save = False
+                return self.stored_path
         elif workflow_dir:
-            path = os.path.join(workflow_dir, self.stored_path)
-        else:
-            path = None
+            return os.path.normpath(
+                os.path.join(workflow_dir, self.stored_path))
 
-        if path and os.path.exists(path):
-            return path
-        else:
-            self.stored_path = workflow_dir or _userhome
-            self.auto_save = False
-            return self.stored_path
+        self.stored_path = workflow_dir or _userhome
+        self.auto_save = False
+        return self.stored_path
 
     @property
     def filename(self):


Test patch:
diff --git a/Orange/widgets/utils/save/tests/test_owsavebase.py b/Orange/widgets/utils/save/tests/test_owsavebase.py
--- a/Orange/widgets/utils/save/tests/test_owsavebase.py
+++ b/Orange/widgets/utils/save/tests/test_owsavebase.py
@@ -103,7 +103,8 @@ def assertPathEqual(self, a, b):
             b = b.replace("\\", "/")
         self.assertEqual(a.rstrip("/"), b.rstrip("/"))
 
-    @patch("os.path.exists", lambda name: name == "/home/u/orange/a/b")
+    @patch("os.path.exists",
+           lambda name: name in ["/home/u/orange/a/b", "/foo/bar"])
     def test_open_moved_workflow(self):
         """Stored relative paths are properly changed on load"""
         home = _userhome
@@ -128,6 +129,15 @@ def test_open_moved_workflow(self):
             self.assertPathEqual(w.filename, home_c_foo)
             self.assertFalse(w.auto_save)
 
+            w = self.create_widget(
+                self.OWSaveMockWriter,
+                stored_settings=dict(stored_path="/foo/bar",
+                                     stored_name="c.foo",
+                                     auto_save=True))
+            self.assertPathEqual(w.last_dir, "/foo/bar")
+            self.assertPathEqual(w.filename, "/foo/bar/c.foo")
+            self.assertFalse(w.auto_save)
+
             w = self.create_widget(
                 self.OWSaveMockWriter,
                 stored_settings=dict(stored_path=".",
@@ -162,9 +172,9 @@ def test_open_moved_workflow(self):
                 stored_settings=dict(stored_path="a/d",
                                      stored_name="c.foo",
                                      auto_save=True))
-            self.assertPathEqual(w.last_dir, "/home/u/orange/")
-            self.assertPathEqual(w.filename, "/home/u/orange/c.foo")
-            self.assertFalse(w.auto_save)
+            self.assertPathEqual(w.last_dir, "/home/u/orange/a/d")
+            self.assertPathEqual(w.filename, "/home/u/orange/a/d/c.foo")
+            self.assertTrue(w.auto_save)
 
             w = self.create_widget(
                 self.OWSaveMockWriter,
@@ -182,7 +192,7 @@ def test_open_moved_workflow(self):
                                      auto_save=True))
             self.assertPathEqual(w.last_dir, "/home/u/orange/")
             self.assertPathEqual(w.filename, "/home/u/orange/c.foo")
-            self.assertFalse(w.auto_save)
+            self.assertTrue(w.auto_save)
 
             w = self.create_widget(
                 self.OWSaveMockWriter,
@@ -191,7 +201,7 @@ def test_open_moved_workflow(self):
                                      auto_save=True))
             self.assertPathEqual(w.last_dir, "/home/u/orange/")
             self.assertPathEqual(w.filename, "/home/u/orange/c.foo")
-            self.assertFalse(w.auto_save)
+            self.assertTrue(w.auto_save)
 
     def test_move_workflow(self):
         """Widget correctly stores relative paths"""