diff --git a/filemanager/inc/class.filemanager_collab.inc.php b/filemanager/inc/class.filemanager_collab.inc.php index 31f825190e..4ca4e47c16 100644 --- a/filemanager/inc/class.filemanager_collab.inc.php +++ b/filemanager/inc/class.filemanager_collab.inc.php @@ -60,7 +60,7 @@ class filemanager_collab extends filemanager_collab_bo { */ function leave_session ($es_id, $member_id) { - if (!$this->is_sessionValid($es_id)) throw new Exception ('Session is not valid!'); + if (!$this->is_sessionValid($es_id) || !$this->is_memberValid($es_id, $member_id)) throw new Exception ('Session is not valid!'); return array ( 'session_id' => $es_id, 'memberid' => $member_id, @@ -95,6 +95,17 @@ class filemanager_collab extends filemanager_collab_bo { $memberid = $params['args']['member_id']? $params['args']['member_id']: ''; $es_id = $params['args']['es_id']; $seq_head = (string) isset($params['args']['seq_head'])? $params['args']['seq_head']: null; + // we need to inform clients about session changes to update themselves + // based on that. For instance, after discarding changes other participants + // should get notified to reload their session. + if (!$this->is_memberValid($es_id, $memberid)) { + $response = array ( + 'result' => 'error', + 'error' => 'ENOSESSION' + ); + throw new Exception('Session is not valid!'); + } + if(!is_null($seq_head)) { $client_ops = $params['args']['client_ops']? $params['args']['client_ops']: []; @@ -163,6 +174,8 @@ class filemanager_collab extends filemanager_collab_bo { } /** + * Ajax function to handle actions called by client-side + * types: save, delete, discard * * @param type $es_id * @param type $action @@ -177,6 +190,11 @@ class filemanager_collab extends filemanager_collab_bo { case 'delete': $this->SESSION_cleanup($es_id); break; + case 'discard': + $this->OP_Discard($es_id); + break; + default: + // } } @@ -211,6 +229,20 @@ class filemanager_collab extends filemanager_collab_bo { return $session? true : false; } + /** + * Check if the member id of the session has valid status + * status: 1 is valid 0 is invalid + * + * @param type $es_id + * @param type $member_id + * @return boolean returns true if it's valid + */ + function is_memberValid ($es_id, $member_id) + { + $member = $this->MEMBER_getMember($es_id, $member_id); + return $member && $member['status'] != 0 ? true: false; + } + /** * Function to get genesis url by generating a temp genesis temp file * out of given path, and returnig es_id md5 hash and genesis url to diff --git a/filemanager/inc/class.filemanager_collab_bo.inc.php b/filemanager/inc/class.filemanager_collab_bo.inc.php index be7c682aed..1e7ea5e205 100644 --- a/filemanager/inc/class.filemanager_collab_bo.inc.php +++ b/filemanager/inc/class.filemanager_collab_bo.inc.php @@ -231,6 +231,35 @@ class filemanager_collab_bo $this->OP_add2Db($op, $es_id); } + /** + * Function to discard all changes applied to a session + * @param type $es_id + */ + public function OP_Discard ($es_id) + { + $this->db->update( + self::MEMBER_TABLE, + array('collab_status' => 0), + array('collab_es_id' => $es_id), + __LINE__, + __FILE__, + 'filemanager' + ); + $this->db->delete( + self::OP_TABLE, + array('collab_es_id' => $es_id), + __LINE__, + __FILE__, + 'filemanager' + ); + $this->db->delete( + self::SESSION_TABLE, + array('collab_es_id' => $es_id), + __LINE__, + __FILE__ + ); + } + /** * Function to remove member from list * @@ -405,6 +434,28 @@ class filemanager_collab_bo return is_array($member)? $member['collab_member_id']: null; } + /** + * Function to get member record of a session + * + * @param string $es_id session id + * + * @return array array of records or null + * @throws Exception throws exception if no member id is given + */ + protected function MEMBER_getMember ($es_id, $member_id) + { + if (!$es_id) throw new Exception (self::EXCEPTION_MESSAGE_NO_SESSION); + $query = $this->db->select( + self::MEMBER_TABLE, + '*', + array('collab_es_id' => $es_id, 'collab_member_id' => $member_id), + __LINE__, + __FILE__ + ); + $member = $query->fetchRow(); + return is_array($member)? self::db2id($member): null; + } + /** * Utility function to map DB fields to ids * diff --git a/filemanager/inc/class.filemanager_ui.inc.php b/filemanager/inc/class.filemanager_ui.inc.php index 36d8d33561..7be8d17f7b 100644 --- a/filemanager/inc/class.filemanager_ui.inc.php +++ b/filemanager/inc/class.filemanager_ui.inc.php @@ -1505,6 +1505,7 @@ class filemanager_ui if (!Api\Vfs::check_access($paths[1], Api\Vfs::WRITABLE)) { unset ($actions['save']); + unset ($actions['discard']); unset ($actions['delete']); } $tmpl->setElementAttribute('tools', 'actions', $actions); @@ -1558,6 +1559,14 @@ class filemanager_ui 'onExecute' => 'javaScript:app.filemanager.editor_delete', 'allowOnMultiple' => false, 'toolbarDefault' => false + ), + 'discard' => array( + 'caption' => 'Discard', + 'icon' => 'delete', + 'group' => ++$group, + 'onExecute' => 'javaScript:app.filemanager.editor_discard', + 'allowOnMultiple' => false, + 'toolbarDefault' => false ) ); return $actions; diff --git a/filemanager/js/collab.js b/filemanager/js/collab.js index 0b586c2ce3..b0109dbd87 100644 --- a/filemanager/js/collab.js +++ b/filemanager/js/collab.js @@ -365,6 +365,39 @@ app.classes.filemanager = app.classes.filemanager.extend({ return path; }, + /** + * This function gets called after discard action to + * notify particioant to join to the new session or + * save as the document to not lose changes. + * + */ + editor_discarded: function () + { + var self = this; + var buttons = [ + {"button_id": 1,"text": 'reload', id: 'reload', image: 'check' }, + {"button_id": 0,"text": 'save as', id: 'save', image: 'cancel', "default":true} + ] + et2_dialog.show_dialog( + function(_btn) + { + if (_btn == 'save') + { + self.editor_save({id:'saveas'}); + } + else if (_btn == 'reload') + { + window.location.reload(); + } + }, + egw.lang('All the changes has been discarded and new session created! Save as your local changes if you need them or reload to join new session.'), + 'Delete file', + null, + buttons, + et2_dialog.WARNING_MESSAGE + ); + }, + /** * Function to create collab editor * @@ -384,11 +417,24 @@ app.classes.filemanager = app.classes.filemanager.extend({ /** * Editor error handler function - * @param {type} e + * + * this function also been used in order to notify + * participant about session changes. + * + * @param {string} e */ function handleEditingError (e) { - console.log(e) + switch (e) + { + // This type of error happens when the session is discarded or + // the document has been deleted and all records in database's been removed. + case 'sessionDoesNotExist': + this.editor_discarded(); + break; + default: + console.log(e) + } }; function onEditing () @@ -412,7 +458,7 @@ app.classes.filemanager = app.classes.filemanager.extend({ return; } self.editor = _editor; - self.editor.addEventListener(Wodo.EVENT_UNKNOWNERROR, handleEditingError); + self.editor.addEventListener(Wodo.EVENT_UNKNOWNERROR, jQuery.proxy(handleEditingError, self)); self.editor.joinSession(serverFactory.createSessionBackend(sessionId, memberId, server), onEditing); }; @@ -467,8 +513,42 @@ app.classes.filemanager = app.classes.filemanager.extend({ switch(_data.action) { case 'delete': - if (!_data.errs) egw.json('filemanager.filemanager_collab.ajax_actions', [this.collab_server.es_id, 'delete']).sendRequest(); + if (!_data.errs) egw.json('filemanager.filemanager_collab.ajax_actions', [this.collab_server.es_id, 'delete'], function(){window.close();}).sendRequest(); } + }, + + /** + * Discard stacked modification in session from all participants + * it will warn user about the consequences which would be removing + * all stored OP modfifications in DB. Then as result it will notify + * other participants about the action and prompt them to reload the + * session or save as the current session if they want to keep their + * changes. + * + */ + editor_discard: function () + { + var self = this; + var buttons = [ + {"button_id": 1,"text": 'discard', id: 'discard', image: 'check' }, + {"button_id": 0,"text": 'cancel', id: 'cancel', image: 'cancel', "default":true} + ] + et2_dialog.show_dialog( + function(_btn) + { + if (_btn == 'discard') + { + egw.json('filemanager.filemanager_collab.ajax_actions',[self.collab_server.es_id, 'discard'], function(){ + window.location.reload(); + }).sendRequest(); + } + }, + egw.lang('You are about to discard all modifications applied to this document by you and other participants. Be aware this will affect all participants and their changes on this document too.'), + 'Discard all changes', + null, + buttons, + et2_dialog.WARNING_MESSAGE + ); } });