W.I.P. collab editor: First implementation for syncing operation

This commit is contained in:
Hadi Nategh 2016-08-12 16:29:55 +02:00
parent 00ce2295be
commit 99b8c5773f
4 changed files with 258 additions and 29 deletions

View File

@ -0,0 +1,144 @@
<?php
/**
* EGroupware - Filemanager Collab
*
* @link http://www.egroupware.org
* @package filemanager
* @author Hadi Nategh <hn-AT-stylite.de>
* @copyright (c) 2016 by Stylite AG
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
use EGroupware\Api;
class filemanager_collab extends filemanager_collab_bo {
/**
* Methods callable via menuaction
*
* @var array
*/
var $public_functions = array(
'poll' => true
);
/**
* Constructor
*
*/
function __construct()
{
parent::__construct();
}
/**
* Join session, initialises edit session for opened file by user
*
* @param type $es_id session id
* @return array returns an array consists of session data
*/
function join_session ($es_id)
{
$response = $this->initSession($es_id);
$response += array (
'id' => $GLOBALS['egw_info']['user']['account_id'],
'full_name' => $GLOBALS['egw_info']['user']['account_fullname'],
'success' => true
);
return $response;
}
/**
* Polling mechanisim to sysncronise data
*
* @throws Exception
*/
function poll ()
{
// Get POST request payload
$payload = file_get_contents('php://input');
$params = $payload? json_decode ($payload, true): null;
$response = array();
if (is_array($params))
{
switch ($params['command'])
{
case 'join_session':
$response = $this->join_session($params['args']['es_id'],$params['args']['user_id']);
break;
case 'sync_ops':
try
{
$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;
if(!is_null($seq_head))
{
$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 (count($client_ops)>0)
{
$newHead = $this->get_newHead($es_id, $memberid, $client_ops);
$response = array(
'result' => 'added',
'head_seq' => $newHead ? $newHead : $current_seq_head
);
}
else
{
$response = array(
'result' => 'new_ops',
'ops' => array(),
'head_seq' => $current_seq_head
);
}
}
else
{
$response = array(
'result' => count($client_ops)>0 ? 'conflict' : 'new_ops',
'ops' => $this->OP_getOPSECS($es_id, $seq_head),
'head_seq' => $current_seq_head,
);
}
}
else
{
throw new Exception('Invalid seq head!');
}
} catch (Exception $ex) {
error_log($ex->getMessage());
}
break;
default:
//
}
}
header('content-type: application/json; charset=utf-8');
echo json_encode($response);
exit();
}
/**
* Function to get a new head sequence
*
* @param string $es_id session id
* @param string $member_id member id
* @param array $client_ops opspec from client side
*
* @return string return a seq head number
*/
function get_newHead ($es_id, $member_id, $client_ops)
{
$this->OP_addOPS($es_id, $member_id, $client_ops);
return $this->OP_getHeadSeq($es_id);
}
}

View File

@ -58,22 +58,28 @@ class filemanager_collab_bo
*/
protected function initSession ($es_id)
{
$session = $this->db->select(self::SESSION_TABLE,'*', array('collab_es_id' => $es_id));
$query = $this->db->select(self::SESSION_TABLE,'*', array('collab_es_id' => $es_id),__LINE__,__FILE__);
$full_name = $GLOBALS['egw_info']['user']['account_fullname'];
$color = $GLOBALS['egw_info']['user']['prefs']['collab_user_color'];
$color = $GLOBALS['egw_info']['user']['preferences']['filemanager']['collab_user_color'];
$user_id = $GLOBALS['egw_info']['user']['account_id'];
$imageUrl = $GLOBALS['egw_info']['server']['webserver_url'].'/index.php?menuaction=addressbook.addressbook_ui.photo&account_id='.$user_id;
if (!($result = self::db2id($session->fields)) && $es_id)
if (!($result = self::db2id($query->fetchRow())) && $es_id)
{
$result = self::db2id($this->SESSION_add2Db($es_id));
}
$this->MEMBER_add2Db($es_id, $user_id, $color);
if (is_null($result['member_id'] = $this->MEMBER_getUserMemberId($es_id, $user_id)))
{
$this->MEMBER_add2Db($es_id, $user_id, $color);
$member_id = $this->MEMBER_getLastMember();
$result['member_id'] = $this->MEMBER_getLastMember();
$this->OP_addMember($es_id, $member_id, $full_name, $user_id, $color);
$this->OP_addMember($es_id, $result['member_id'], $full_name, $user_id, $color, $imageUrl);
}
return $result;
}
/**
@ -99,7 +105,6 @@ class filemanager_collab_bo
return $data;
}
/**
* OP addMember function is backend equivalent to addMember from frontend
*
@ -110,12 +115,12 @@ class filemanager_collab_bo
* @param string $color
* @param string $imageUrl
*/
protected function OP_addMember($es_id, $member_id, $full_name, $user_id, $color, $imageUrl)
protected function OP_addMember($es_id, $member_id, $full_name, $user_id, $color='', $imageUrl='')
{
$date = new DateTime();
$op = array(
'optype' => 'AddMember',
'member' => (string) $member_id,
'memberid' => (string) $member_id,
'timestamp' => $date->getTimestamp(),
'setProperties' => array(
'fullName' => $full_name,
@ -139,7 +144,7 @@ class filemanager_collab_bo
{
if (!$es_id) throw new Exception ('Session id must be given, none given!');
$headSeq = $this->db->select(
$query = $this->db->select(
self::OP_TABLE,
'collab_seq',
array('collab_es_id' => $es_id),
@ -149,8 +154,58 @@ class filemanager_collab_bo
'ORDER BY collab_seq DESC LIMIT 1',
'filemanager'
);
$head_seq = $query->fetchRow();
return is_array($head_seq)? $head_seq['collab_seq']: '';
}
return $headSeq->fields? $headSeq->fields['collab_seq']: '';
/**
* Function to get opsepcs of session base on seq head
*
* @param string $es_id session id
* @param string $seq_head sequence head
*
* @return array returns array of opspecs
*/
protected function OP_getOPSECS($es_id, $seq_head)
{
if ($seq_head == "")
{
$seq_head = -1;
}
$ops = array();
$query = $this->db->select(
self::OP_TABLE,
'collab_opspec',
'collab_es_id ="'. $es_id.'" AND collab_seq >'.$seq_head,
__LINE__,
__FILE__,
false, 'ORDER BY collab_seq ASC','filemanager');
foreach ($query as $spec)
{
$op = json_decode($spec['collab_opspec'], true);
$op['memberid'] = strval($op['memberid']);
$ops [] = $op;
}
return $ops;
}
/**
* Function to ops data array into op table
*
* @param string $es_id session id
* @param string $member_id member id
* @param array $client_ops array of ops
* @throws Exception if no array of ops given
*/
protected function OP_addOPS ($es_id, $member_id, $client_ops)
{
if (count($client_ops)<= 0) throw new Exception ('ops need to be an array of op data, none array given!');
foreach ($client_ops as $op)
{
$this->OP_add2Db($op, $es_id);
}
}
/**
@ -163,7 +218,7 @@ class filemanager_collab_bo
{
$data = array (
'collab_es_id' => $es_id,
'collab_member' => $op['member'],
'collab_member' => $op['memberid'],
'collab_optype' => $op['optype'],
'collab_opspec' => json_encode($op)
);
@ -192,21 +247,21 @@ class filemanager_collab_bo
*
* @param string $member_id member id
*
* @return array returns an array consist of member record
* @return string member id or null
* @throws Exception throws exception if no member id is given
*/
protected function MEMBER_getMember ($member_id)
protected function MEMBER_getUserMemberId ($es_id, $user_id)
{
if (!$member_id) throw new Exception ('Member id must be given, none given!');
$member = $this->db->select(
if (!$es_id || !$user_id) throw new Exception ('Member id must be given, none given!');
$query = $this->db->select(
self::MEMBER_TABLE,
'*',
array('collab_member_id' => $member_id),
'collab_member_id',
array('collab_es_id' => $es_id, 'collab_uid' => $user_id),
__LINE__,
__FILE__
);
return $member->fields? self::db2id($member->fileds): [];
$member = $query->fetchRow();
return is_array($member)? $member['collab_member_id']: null;
}
/**
@ -230,7 +285,7 @@ class filemanager_collab_bo
*/
protected function MEMBER_getLastMember()
{
$last_row = $this->db->select(
$query = $this->db->select(
self::MEMBER_TABLE,
'collab_member_id',
'',
@ -239,7 +294,7 @@ class filemanager_collab_bo
false,
"ORDER BY `collab_member_id` DESC LIMIT 1;"
);
return $last_row->fields? $last_row->fields['collab_member_id']: 0;
$last_row = $query->fetchRow();
return is_array($last_row)? $last_row['collab_member_id']: 0;
}
}

View File

@ -124,6 +124,13 @@ class filemanager_hooks
);
$settings = array(
'sections.1' => array(
'type' => 'section',
'title' => lang('General settings'),
'no_lang'=> true,
'xmlrpc' => False,
'admin' => False
),
'startfolder' => array(
'type' => 'input',
'name' => 'startfolder',
@ -227,6 +234,26 @@ class filemanager_hooks
'admin' => False,
);
}
$settings += array (
'sections.2' => array(
'type' => 'section',
'title' => lang('Collab Editor settings'),
'no_lang'=> true,
'xmlrpc' => False,
'admin' => False
),
'collab_user_color' => array(
'type' => 'color',
'label' => lang('User color indicator'),
'name' => 'collab_user_color',
'help' => lang('Use eg. %1 or %2','#FF0000','orange'),
'no_lang'=> true,
'xmlrpc' => True,
'admin' => False,
)
);
return $settings;
}

View File

@ -10,8 +10,9 @@
*/
/*egw:uses
/api/js/webodf/wodotexteditor/wodotexteditor/wodotexteditor.js;
/api/js/webodf/wodotexteditor/wodotexteditor/webodf.js;
/api/js/webodf/collab/webodf.js;
/api/js/webodf/collab/wodocollabtexteditor.js;
/api/js/webodf/collab/dojo-amalgamation.js;
*/
/**
@ -119,7 +120,7 @@ app.classes.filemanager = AppJS.extend(
{
// need to make body rock solid to avoid extra scrollbars
jQuery('body').css({overflow:'hidden'});
this._init_odf_editor ();
this._init_odf_collab_editor ();
}
},
@ -1082,10 +1083,12 @@ app.classes.filemanager = AppJS.extend(
var serverOptions = {
"serverParams": {
url:egw.webserverUrl+'/api/js/webodf/poll.php?action=poll',
url:egw.link('/index.php?', {
menuaction: 'filemanager.filemanager_collab.poll'
}),
genesisUrl:egw.webserverUrl+file_path
},
"sessionId": base64.toBase64(egw.webserverUrl+file_path),
"sessionId": egw.webserverUrl+file_path,
editorOptions: {
allFeaturesEnabled: true,
userData: {
@ -1332,7 +1335,7 @@ app.classes.filemanager = AppJS.extend(
serverParams = _args.serverParams,
sessionId = _args.sessionId,
editorOptions = jQuery.extend(_args.editorOptions,{networkSecurityToken:'', closeCallback:this.editor_close}),
userId = egw.user('account_lid'),
userId = egw.user('account_id'),
memberId,
self = this;