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)
This commit is contained in:
Hadi Nategh 2016-08-17 17:23:22 +02:00
parent 94bc949cfa
commit 307e59e7ab
6 changed files with 139 additions and 48 deletions

View File

@ -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;
}
}

View File

@ -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
*

View File

@ -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);
}

View File

@ -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', {

View File

@ -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({
});
});
}
});

View File

@ -54,7 +54,7 @@
<buttononly statustext="Tile view" id="button[change_view]" onclick="app.filemanager.change_view" image="list_tile" background_image="true"/>
</template>
<template id="filemanager.index.header_right" template="" lang="" group="0" version="1.9.003">
<buttononly statustext="Create new open document file" id="button[new_doc]" onclick="app.filemanager.list_editor_new();" image="new" background_image="true"/>
<buttononly statustext="Create new open document file" id="button[new_doc]" onclick="app.filemanager.editor_new();" image="new" background_image="true"/>
<buttononly statustext="Rename, change permissions or ownership" id="button[edit]" onclick="app.filemanager.editprefs();" image="edit" background_image="true"/>
<buttononly statustext="Create directory" id="button[createdir]" onclick="app.filemanager.createdir();" image="button_createdir" ro_image="createdir_disabled" background_image="true"/>
<buttononly statustext="Create a link" id="button[symlink]" onclick="app.filemanager.symlink();" image="link" ro_image="link_disabled" background_image="true"/>