From 307e59e7ab18e94f0cc2c842d44550be05a2274c Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Wed, 17 Aug 2016 17:23:22 +0200 Subject: [PATCH] W.I.P collab Editor: - Implement user access to a document - Implement leave session mechanism for user - Implement single document network for an user (user be able to open a doc multiple times from same or other instances and get them all synced) --- .../inc/class.filemanager_collab.inc.php | 55 +++++++++++++++-- .../inc/class.filemanager_collab_bo.inc.php | 54 +++++++++++++--- filemanager/inc/class.filemanager_ui.inc.php | 13 +++- filemanager/js/app.js | 2 +- filemanager/js/collab.js | 61 +++++++++---------- filemanager/templates/default/index.xet | 2 +- 6 files changed, 139 insertions(+), 48 deletions(-) diff --git a/filemanager/inc/class.filemanager_collab.inc.php b/filemanager/inc/class.filemanager_collab.inc.php index 9384df3b3b..1838f1c903 100644 --- a/filemanager/inc/class.filemanager_collab.inc.php +++ b/filemanager/inc/class.filemanager_collab.inc.php @@ -40,16 +40,37 @@ class filemanager_collab extends filemanager_collab_bo { */ function join_session ($es_id) { - $response = $this->initSession($es_id); + $paths = explode('/webdav.php', $es_id); + if (Api\Vfs::check_access($paths[1], Api\Vfs::READABLE)) + { + $response = $this->initSession($es_id); + $response['success'] = true; + } $response += array ( 'id' => $GLOBALS['egw_info']['user']['account_id'], - 'full_name' => $GLOBALS['egw_info']['user']['account_fullname'], - 'success' => true + 'full_name' => $GLOBALS['egw_info']['user']['account_fullname'] ); return $response; } + /** + * This function gets called when user leaves the session + * + * @param string $es_id + * @param string $member_id + * + * @return array returns an array of data as response for client-side + */ + function leave_session ($es_id, $member_id) + { + return array ( + 'session_id' => $es_id, + 'memberid' => $member_id, + 'success' => $this->OP_removeMember($es_id, $member_id) + ); + } + /** * Polling mechanisim to sysncronise data * @@ -63,11 +84,15 @@ class filemanager_collab extends filemanager_collab_bo { $response = array(); if (is_array($params)) { + $paths = explode('/webdav.php', $params['args']['es_id']); switch ($params['command']) { case 'join_session': $response = $this->join_session($params['args']['es_id'],$params['args']['user_id']); break; + case 'leave_session': + $response = $this->leave_session($params['args']['es_id'],$params['args']['member_id']); + break; case 'sync_ops': try { @@ -78,7 +103,7 @@ class filemanager_collab extends filemanager_collab_bo { { $client_ops = $params['args']['client_ops']? $params['args']['client_ops']: []; $current_seq_head = $this->OP_getHeadSeq($es_id); - if ($seq_head == $current_seq_head) { + if ($seq_head == $current_seq_head && $this->is_collabAllowed($es_id)) { if (count($client_ops)>0) { @@ -141,4 +166,26 @@ class filemanager_collab extends filemanager_collab_bo { return $this->OP_getHeadSeq($es_id); } + /** + * + * @param type $es_id + * @param type $action + */ + function ajax_actions ($es_id, $action) + { + switch ($action) + { + case 'save': + $this->SESSION_Save($es_id); + break; + } + } + + function is_collabAllowed ($es_id) + { + $paths = explode('/webdav.php', $es_id); + $allowed = Api\Vfs::check_access($paths[1], Api\Vfs::WRITABLE) && + !preg_match('/\/api\/js\/webodf\/template.odf$/', $es_id); + return $allowed; + } } \ No newline at end of file diff --git a/filemanager/inc/class.filemanager_collab_bo.inc.php b/filemanager/inc/class.filemanager_collab_bo.inc.php index 8e869ee648..227ca27249 100644 --- a/filemanager/inc/class.filemanager_collab_bo.inc.php +++ b/filemanager/inc/class.filemanager_collab_bo.inc.php @@ -80,14 +80,12 @@ class filemanager_collab_bo $result = self::db2id($this->SESSION_add2Db($es_id)); } - if (is_null($result['member_id'] = $this->MEMBER_getUserMemberId($es_id, $user_id))) - { - $this->MEMBER_add2Db($es_id, $user_id, $color); - $result['member_id'] = $this->MEMBER_getLastMember(); + $this->MEMBER_add2Db($es_id, $user_id, $color); - $this->OP_addMember($es_id, $result['member_id'], $full_name, $user_id, $color, $imageUrl); - } + $result['member_id'] = $this->MEMBER_getLastMember(); + + $this->OP_addMember($es_id, $result['member_id'], $full_name, $user_id, $color, $imageUrl); return $result; } @@ -212,6 +210,24 @@ class filemanager_collab_bo $this->OP_add2Db($op, $es_id); } + /** + * Function to remove member from list + * + * @param string $es_id session id + * @param string $member_id membe id + * + * @return boolean returns true if remove member is successful otherwise false + */ + function OP_removeMember ($es_id, $member_id) + { + $op = array ( + 'optype' => 'RemoveMember', + 'memberid' => (string) $member_id, + 'timestamp' => self::getTimeStamp() + ); + return $this->OP_add2Db($op, $es_id)?true:false; + } + /** * Function to get top head seq for a given session * @@ -302,7 +318,7 @@ class filemanager_collab_bo 'collab_optype' => $op['optype'], 'collab_opspec' => json_encode($op) ); - $this->db->insert(self::OP_TABLE, $data,false,__LINE__, __FILE__,'filemanager'); + return $this->db->insert(self::OP_TABLE, $data,false,__LINE__, __FILE__,'filemanager'); } /** @@ -344,6 +360,30 @@ class filemanager_collab_bo return is_array($member)? $member['collab_member_id']: null; } + /** + * Function to get member record of specific member id + * + * @param string $member_id member id + * + * @return string member id or null + * @throws Exception throws exception if no member id is given + */ + protected function MEMBER_getUserLastMemberId ($es_id, $user_id) + { + if (!$es_id || !$user_id) throw new Exception (self::EXCEPTION_MESSAGE_NO_SESSION); + $query = $this->db->select( + self::MEMBER_TABLE, + 'collab_member_id', + array('collab_es_id' => $es_id, 'collab_uid' => $user_id), + __LINE__, + __FILE__, + false, + "ORDER BY `collab_member_id` DESC LIMIT 1;" + ); + $member = $query->fetchRow(); + return is_array($member)? $member['collab_member_id']: 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 8b85d2f460..7befe41402 100644 --- a/filemanager/inc/class.filemanager_ui.inc.php +++ b/filemanager/inc/class.filemanager_ui.inc.php @@ -17,7 +17,7 @@ use EGroupware\Api\Egw; use EGroupware\Api\Etemplate; use EGroupware\Api\Vfs; -use filemanager_collab_bo; + /** * Filemanage user interface class */ @@ -1485,14 +1485,21 @@ class filemanager_ui { $tmpl = new Etemplate('filemanager.editor'); $file_path = $_GET['path']; + $paths = explode('/webdav.php', $file_path); // Include css files used by wodocollabeditor Api\Framework::includeCSS('/api/js/webodf/collab/app/resources/app.css'); Api\Framework::includeCSS('/api/js/webodf/collab/wodocollabpane.css'); Api\Framework::includeCSS('/api/js/webodf/collab/wodotexteditor.css'); Api\Framework::includeJS('/filemanager/js/collab.js',null, 'filemanager'); - $tmpl->setElementAttribute('tools', 'actions', self::getActions_edit()); - $preserve = $content = array('file_path' => $file_path); + $actions = self::getActions_edit(); + if (!Api\Vfs::check_access($paths[1], Api\Vfs::WRITABLE)) + { + unset ($actions['save']); + unset ($actions['delete']); + } + $tmpl->setElementAttribute('tools', 'actions', $actions); + $preserve = $content = array('file_path' => $file_path); $tmpl->exec('filemanager.filemanager_ui.editor',$content,array(),array(),$preserve,2); } diff --git a/filemanager/js/app.js b/filemanager/js/app.js index c9fe8a0917..608e6d61de 100644 --- a/filemanager/js/app.js +++ b/filemanager/js/app.js @@ -1016,7 +1016,7 @@ app.classes.filemanager = AppJS.extend( * * @todo: creating new empty odt file */ - list_editor_new: function (_egwAction) { + editor_new: function (_egwAction) { var self = this, template_url = '/api/js/webodf/template.odt'; egw.open_link(egw.link('/index.php', { diff --git a/filemanager/js/collab.js b/filemanager/js/collab.js index 43f6e64d17..f9d77f1001 100644 --- a/filemanager/js/collab.js +++ b/filemanager/js/collab.js @@ -8,6 +8,7 @@ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ */ + /*egw:uses /filemanager/js/collab_config.js; /api/js/webodf/collab/dojo-amalgamation.js; @@ -23,12 +24,20 @@ */ app.classes.filemanager = app.classes.filemanager.extend({ /* - * odf editor object + * @var editor odf editor object */ editor: {}, + /** + * @var regexp for acceptable mime types + */ editor_mime: RegExp(/application\/vnd\.oasis\.opendocument\.text/), + /** + * @var collab_server server object + */ + collab_server: {}, + /** * Destructor */ @@ -36,6 +45,7 @@ app.classes.filemanager = app.classes.filemanager.extend({ { delete this.editor; delete editor_mime; + delete collab_server; // call parent this._super.apply(this, arguments); }, @@ -58,7 +68,7 @@ app.classes.filemanager = app.classes.filemanager.extend({ // need to make body rock solid to avoid extra scrollbars jQuery('body').css({overflow:'hidden'}); var self = this; - jQuery(window).on('unload', function(){self.editor.leaveSession()}); + jQuery(window).on('unload', function(){self.editor_leaveSession()}); this._init_odf_collab_editor (); } }, @@ -136,6 +146,16 @@ app.classes.filemanager = app.classes.filemanager.extend({ } }, + /** + * Function to leave the current editing session + * and as result it will call client-side and server leave session. + */ + editor_leaveSession: function () + { + this.editor.leaveSession(function(){}); + this.collab_server.server.leaveSession(this.collab_server.es_id, this.collab_server.memberid); + }, + /** * Method to close an opened document * @@ -151,7 +171,7 @@ app.classes.filemanager = app.classes.filemanager.extend({ { var closeFn = function () { - self.editor.leaveSession(function(){}); + self.editor_leaveSession(); if (action != 'new') { window.close(); @@ -184,33 +204,6 @@ app.classes.filemanager = app.classes.filemanager.extend({ } }, - /** - * Method to create a new document - * @param {object} _egwAction egw action object - * - * @todo: creating new empty odt file - */ - editor_new: function (_egwAction) { - var self = this, - template_url = '/api/js/webodf/template.odt'; - - if (Object.keys(this.editor).length > 0) - { - this.editor_close(_egwAction, function(){ - - // TODO create new temp file - - }); - } - else - { - egw.open_link(egw.link('/index.php', { - menuaction: 'filemanager.filemanager_ui.editor', - path: template_url - }), '', egw.link_get_registry('filemanager','view_popup')); - } - }, - /** * Method call for saving edited document * @@ -239,6 +232,7 @@ app.classes.filemanager = app.classes.filemanager.extend({ success: function(data) { egw(window).message(egw.lang('Document %1 successfully has been saved.', filename[1])); self.editor.setDocumentModified(false); + egw.json('filemanager.filemanager_collab.ajax_actions',[self.editor_getSessionId(), 'save']).sendRequest(); }, error: function () {}, data: blob, @@ -270,7 +264,7 @@ app.classes.filemanager = app.classes.filemanager.extend({ // Add odt extension if not exist if (!file_path.match(/\.odt$/,'ig')) file_path += '.odt'; widgetFilePath.set_value(file_path); - self.editor.leaveSession(function(){}); + self.editor_leaveSession(); self.editor.getDocumentAsByteArray(saveByteArrayLocally); self._init_odf_collab_editor(); egw.refresh('','filemanager'); @@ -429,8 +423,12 @@ app.classes.filemanager = app.classes.filemanager.extend({ function joinSession(_sessionId) { var sid = _sessionId; + server.joinSession(userId, sid, function (_memberId) { memberId = _memberId; + // Set server object for current session + self.collab_server = {server:server, memberid: memberId, es_id: sid}; + if (Object.keys(self.editor).length == 0) { Wodo.createCollabTextEditor('filemanager-editor_odfEditor', editorOptions, onEditorCreated); } else { @@ -459,6 +457,5 @@ app.classes.filemanager = app.classes.filemanager.extend({ }); }); } - }); diff --git a/filemanager/templates/default/index.xet b/filemanager/templates/default/index.xet index 00831cadd5..57ff1e4320 100644 --- a/filemanager/templates/default/index.xet +++ b/filemanager/templates/default/index.xet @@ -54,7 +54,7 @@