Repository: oppia/oppia

Issue:
Implement auto-saving of drafts in the editor
Changes in the editor should be auto-saved so that it's possible for creators to leave the page and resume their editing session at a later time. This also prevents them from having to save frequently because it minimizes the risk that they lose their work due to unforeseen errors. (Note that the autosaving should only happen if the exploration is currently savable, so that the backend storage of the exploration doesn't get corrupted.)
- [x] Backend design doc (@sbhowmik89)
- [x] Backend storage layer (@sbhowmik89)
- [x] Backend domain layer (@sbhowmik89)
- [x] Backend controllers (@sbhowmik89)
- [x] Specification for backend tests (@sbhowmik89)
- [x] Implementation of backend tests (@sbhowmik89)
- [x] Frontend design doc (@526avijitgupta)
- [x] Frontend Services for data (@526avijitgupta)
- [x] Modals to be displayed to the user in case of errors (@526avijitgupta) 
- [x] Modifications to existing e2e tests (@526avijitgupta)
- [ ] Specification for frontend tests (@526avijitgupta)
- [ ] Implementation of frontend tests (@526avijitgupta)



Discussion:
(Noting that #898 is a duplicate of this. I'm reproducing the comments from it in this thread.)

Exploration authors may need to spread the changes out for a commit over more than one editing session. Currently, they are forced to save or publish their changes to the server before they leave. It would be nice if, instead, they could leave and return later, and view the content as they left it. (Of course, if someone else has edited the exploration in the meantime, there will be merge issues and something different would need to happen.)

In addition, this guards against a server error while editing (in which case the client should show a textarea containing records of the edits made, so that they can be reproduced and the work is not lost). More specifically, when the user makes a change in the frontend, the frontend sends the update to the draft-saving service in the backend. Then, if the backend objects or fails to respond, the frontend should use its own changelist to generate a user-readable copy of the lost changes. (The frontend should also probably continue pinging the backend in case the connection loss is only temporary.) Perhaps we can also display to the user the last change that was recorded in the backend, so that they have some idea of what's been lost.

@jacobdavis11 has written a spec for this that is ready to be implemented.

Note also that this feature would fix an issue where people who are working on a single exploration for a long time end up with a timed-out session, losing their work (as reported in #949).

@seanlip I'm working on the design doc for the backend implementation. Should be ready soon because it borrows heavily from @jacobdavis11's  doc. I can take up everything listed under backend components here https://docs.google.com/document/d/1cIGNgtjD-WPFBtNb9HsZAZnmCwQ3QfY3Yg57fYF-w6M/edit#bookmark=id.obadjls6ah81
I can also take on the backend testing.

Design doc for backend:
https://docs.google.com/document/d/1GNJaR7kAUqdLViMGpn_FS9Ks2oja3mhnQUYfNX1Ldgs/edit#

/cc @526avijitgupta 

/cc @razat249 

Hi @razat249, @526avijitgupta -- @sbhowmik89 is now back, so I'm going to suggest that he finish off the backend controllers (and tests), since we do need to get this fix in pretty soon.

@526avijitgupta, if it's OK, please could you continue working on the frontend part of this? @razat249, I'll write to you separately and follow up.

Thanks!

@seanlip Yes it's okay with me to continue working on the frontend part of this.


Resolving patch:
diff --git a/core/controllers/editor.py b/core/controllers/editor.py
--- a/core/controllers/editor.py
+++ b/core/controllers/editor.py
@@ -787,6 +787,7 @@ def post(self, exploration_id):
                 'Cannot revert to version %s from version %s.' %
                 (revert_to_version, current_version))
 
+        exp_services.discard_draft(exploration_id, self.user_id)
         exp_services.revert_exploration(
             self.user_id, exploration_id, current_version, revert_to_version)
         self.render_json({})
@@ -906,19 +907,9 @@ def post(self, exploration_id):
             exploration_id)
 
         if version != current_exploration.version:
-            # TODO(sll): Improve this.
+            # TODO(sll): Improve the handling of merge conflicts.
             self.render_json({
-                'error': (
-                    'Sorry! Someone else has edited and committed changes to '
-                    'this exploration while you were editing it. We suggest '
-                    'opening another browser tab -- which will load the new '
-                    'version of the exploration -- then transferring your '
-                    'changes there. We will try to make this easier in the '
-                    'future -- we have not done it yet because figuring out '
-                    'how to merge different people\'s changes is hard. '
-                    '(Trying to edit version %s, but the current version is '
-                    '%s.).' % (version, current_exploration.version)
-                )
+                'is_version_of_draft_valid': False
             })
         else:
             utils.recursively_remove_key(change_list, '$$hashKey')


Test patch: