2016-08-16 12:47:00 +02:00
/ * *
* EGroupware - Filemanager - Collab editor application object
*
* @ link http : //www.egroupware.org
* @ package filemanager
* @ author Hadi Nategh < hn - AT - stylite . de >
* @ copyright ( c ) 2016 Stylite AG
* @ license http : //opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @ version $Id$
* /
2016-08-17 17:23:22 +02:00
2016-08-16 12:47:00 +02:00
/ * e g w : u s e s
/ f i l e m a n a g e r / j s / c o l l a b _ c o n f i g . j s ;
/ a p i / j s / w e b o d f / c o l l a b / d o j o - a m a l g a m a t i o n . j s ;
/ a p i / j s / w e b o d f / c o l l a b / w e b o d f . j s ;
/ a p i / j s / w e b o d f / c o l l a b / w o d o c o l l a b t e x t e d i t o r . j s ;
/ f i l e m a n a g e r / j s / a p p . j s ;
* /
/ * *
* UI for filemanager collab
*
* @ augments AppJS
* /
app . classes . filemanager = app . classes . filemanager . extend ( {
/ *
2016-08-17 17:23:22 +02:00
* @ var editor odf editor object
2016-08-16 12:47:00 +02:00
* /
editor : { } ,
2016-08-17 17:23:22 +02:00
/ * *
* @ var regexp for acceptable mime types
* /
2016-08-16 12:47:00 +02:00
editor _mime : RegExp ( /application\/vnd\.oasis\.opendocument\.text/ ) ,
2016-08-17 17:23:22 +02:00
/ * *
* @ var collab _server server object
* /
collab _server : { } ,
2016-08-16 12:47:00 +02:00
/ * *
* Destructor
* /
destroy : function ( )
{
delete this . editor ;
delete editor _mime ;
2016-08-17 17:23:22 +02:00
delete collab _server ;
2016-08-16 12:47:00 +02:00
// call parent
this . _super . apply ( this , arguments ) ;
} ,
/ * *
* This function is called when the etemplate2 object is loaded
* and ready . If you must store a reference to the et2 object ,
* make sure to clean it up in destroy ( ) .
*
* @ param et2 etemplate2 Newly ready object
* @ param { string } name template name
* /
et2 _ready : function ( et2 , name )
{
// call parent
this . _super . apply ( this , arguments ) ;
if ( name == "filemanager.editor" )
{
// need to make body rock solid to avoid extra scrollbars
jQuery ( 'body' ) . css ( { overflow : 'hidden' } ) ;
var self = this ;
2016-08-23 18:01:17 +02:00
jQuery ( window ) . on ( 'unload' , function ( ) { self . editor _leaveSession ( ) ; } ) ;
2016-08-25 16:25:26 +02:00
jQuery ( window ) . on ( 'beforeunload' , function ( ) {
if ( ! self . collab _server . close )
{
return true ;
}
else
{
return ;
}
} ) ;
2016-08-16 12:47:00 +02:00
this . _init _odf _collab _editor ( ) ;
}
} ,
/ * *
* Initiate odf collab editor popup & load given file _path as active session
2016-08-23 12:29:49 +02:00
* editors options :
* directParagraphStylingEnabled
* paragraphStyleSelectingEnabled
* paragraphStyleEditingEnabled
* zoomingEnabled
* directTextStylingEnabled
* imageEditingEnabled
* hyperlinkEditingEnabled
* annotationsEnabled
* unstableFeaturesEnabled
* reviewModeEnabled
2016-08-16 12:47:00 +02:00
* /
_init _odf _collab _editor : function ( )
{
2016-08-23 17:57:03 +02:00
var self = this ,
isNew = window . location . href . search ( /&path=/ ) == - 1 ? true : false ;
egw . json ( 'filemanager.filemanager_collab.ajax_getGenesisUrl' , [ this . editor _getFilePath ( ) , isNew ] , function ( _data ) {
2016-08-19 16:38:44 +02:00
var serverOptions = {
serverParams : {
url : egw . link ( '/index.php?' , {
menuaction : 'filemanager.filemanager_collab.poll'
} ) ,
genesisUrl : _data . genesis _url
} ,
sessionId : _data . es _id ,
editorOptions : {
2016-08-23 12:29:49 +02:00
directParagraphStylingEnabled : true ,
paragraphStyleSelectingEnabled : true ,
paragraphStyleEditingEnabled : true ,
zoomingEnabled : true ,
directTextStylingEnabled : true ,
imageEditingEnabled : true ,
hyperlinkEditingEnabled : true ,
annotationsEnabled : true ,
unstableFeaturesEnabled : true ,
// review has to be explicitly disabled to be able to edit the document
reviewModeEnabled : false ,
viewOptions : {
editInfoMarkersInitiallyVisible : true ,
caretAvatarsInitiallyVisible : false ,
caretBlinksOnRangeSelect : true
2016-08-19 16:38:44 +02:00
}
2016-08-16 12:47:00 +02:00
}
2016-08-19 16:38:44 +02:00
} ;
var editor = self . et2 . getWidgetById ( 'odfEditor' ) ;
if ( editor )
{
self . create _collab _editor ( serverOptions ) ;
2016-08-16 12:47:00 +02:00
}
2016-08-19 16:38:44 +02:00
} ) . sendRequest ( ) ;
2016-08-16 12:47:00 +02:00
} ,
2016-08-17 17:23:22 +02:00
/ * *
* Function to leave the current editing session
* and as result it will call client - side and server leave session .
2016-08-19 16:38:44 +02:00
*
* @ param { function } _successCallback function to gets called after leave session is successful
2016-08-17 17:23:22 +02:00
* /
2016-08-25 16:25:26 +02:00
editor _leaveSession : function ( _successCallback , _checkLastActive )
2016-08-17 17:23:22 +02:00
{
2016-08-25 16:25:26 +02:00
var self = this ;
var successCallback = _successCallback || function ( ) { window . close ( ) ; }
var leave = function ( )
{
self . editor . leaveSession ( function ( ) { } ) ;
self . collab _server . server . leaveSession ( self . collab _server . es _id , self . collab _server . memberid , successCallback ) ;
self . collab _server . close = true ;
} ;
if ( ! _checkLastActive )
{
leave ( ) ;
}
egw . json ( 'filemanager.filemanager_collab.ajax_actions' , [ { 'es_id' : this . collab _server . es _id , 'member_id' : this . collab _server . memberid } , 'checkLastMember' ] , function ( _isLastMember ) {
if ( _isLastMember )
{
var buttons = [
{ "button_id" : 3 , "text" : 'save and close' , id : 'save' , image : 'check' } ,
{ "button_id" : 2 , "text" : 'keep unsaved changes and leave' , id : 'leave' , image : 'close' } ,
{ "button_id" : 1 , "text" : 'discard all unsaved changes' , id : 'discard' , image : 'delete' } ,
{ "button_id" : 0 , "text" : 'cancel' , id : 'cancel' , image : 'cancel' , "default" : true }
] ;
et2 _dialog . show _dialog (
function ( _btn )
{
switch ( _btn )
{
case 'save' :
2016-08-25 16:58:19 +02:00
self . editor _save ( { id : 'save' } , function ( ) {
leave ( ) ;
} ) ;
2016-08-25 16:25:26 +02:00
break ;
case 'leave' :
leave ( ) ;
break ;
case 'discard' :
self . editor _discard ( ) ;
break ;
default :
}
} ,
egw . lang ( 'You are the last one on this session. What would you like to do with all changes in this document?' ) ,
'Closing session' ,
null ,
buttons ,
et2 _dialog . WARNING _MESSAGE
) ;
}
else
{
leave ( ) ;
}
} ) . sendRequest ( ) ;
2016-08-17 17:23:22 +02:00
} ,
2016-08-16 12:47:00 +02:00
/ * *
* Method to close an opened document
*
* @ param { object } _egwAction egw action object
* /
2016-08-25 16:25:26 +02:00
editor _close : function ( _egwAction ) {
2016-08-16 12:47:00 +02:00
var self = this ,
action = _egwAction . id ,
2016-08-23 17:57:03 +02:00
file _path = this . et2 . getWidgetById ( 'file_path' ) ;
2016-08-16 12:47:00 +02:00
if ( this . editor )
{
2016-08-25 16:25:26 +02:00
var closeFn = function ( _checkLastActive )
2016-08-16 12:47:00 +02:00
{
2016-08-25 16:25:26 +02:00
self . editor _leaveSession ( null , _checkLastActive ) ;
2016-08-16 12:47:00 +02:00
} ;
2016-08-23 17:57:03 +02:00
// it's an unsaved new file try to warn user about unsaved changes
if ( file _path . value == '' )
2016-08-16 12:47:00 +02:00
{
et2 _dialog . show _dialog (
function ( _btn )
{
if ( _btn == 2 )
{
2016-08-25 16:25:26 +02:00
closeFn ( false ) ;
2016-08-16 12:47:00 +02:00
}
} ,
'There are unsaved changes. Are you sure you want to close this document without saving them?' ,
'unsaved changes' ,
null ,
et2 _dialog . BUTTONS _YES _NO ,
et2 _dialog . WARNING _MESSAGE
) ;
}
else
{
2016-08-25 16:25:26 +02:00
closeFn ( true ) ;
2016-08-16 12:47:00 +02:00
}
}
} ,
/ * *
* Method call for saving edited document
*
* @ param { object } _egwAction egw action object
* /
2016-08-25 16:58:19 +02:00
editor _save : function ( _egwAction , _afterSaveCallback ) {
2016-08-16 12:47:00 +02:00
var self = this ,
widgetFilePath = this . et2 . getWidgetById ( 'file_path' ) ,
2016-08-25 16:58:19 +02:00
file _path = widgetFilePath . value ,
afterSaveCallback = _afterSaveCallback || function ( ) { } ;
2016-08-16 12:47:00 +02:00
2016-08-23 15:26:59 +02:00
2016-08-16 12:47:00 +02:00
if ( this . editor )
{
function saveByteArrayLocally ( err , data ) {
if ( err ) {
alert ( err ) ;
return ;
}
2016-08-23 15:26:59 +02:00
var blob = new Blob ( [ data . buffer ] , { type : self . editor _mime } ) ;
2016-08-16 12:47:00 +02:00
self . editor _file _operation ( {
2016-08-23 15:26:59 +02:00
url : egw . webserverUrl + '/webdav.php' + file _path ,
2016-08-16 12:47:00 +02:00
method : 'PUT' ,
processData : false ,
success : function ( data ) {
2016-11-18 09:46:49 +01:00
egw ( window ) . message ( egw . lang ( 'Document %1 has been saved successfully.' , file _path ) ) ;
2016-08-16 12:47:00 +02:00
self . editor . setDocumentModified ( false ) ;
2016-11-17 13:50:53 +01:00
if ( _egwAction . id == 'saveas' ) return ;
2016-08-25 16:58:19 +02:00
egw . json ( 'filemanager.filemanager_collab.ajax_actions' , [ { 'es_id' : self . collab _server . es _id , 'file_path' : egw . webserverUrl + '/webdav.php' + file _path } , 'save' ] , function ( ) {
afterSaveCallback . call ( self , { } ) ;
} ) . sendRequest ( ) ;
2016-08-16 12:47:00 +02:00
} ,
error : function ( ) { } ,
data : blob ,
mimeType : self . editor _mime
} ) ;
}
//existed file
if ( file _path != '' && _egwAction . id != 'saveas' ) {
this . editor . getDocumentAsByteArray ( saveByteArrayLocally ) ;
}
// new file
else
{
// create file selector
var vfs _select = et2 _createWidget ( 'vfs-select' , {
id : 'savefile' ,
mode : 'saveas' ,
button _caption : "" ,
button _label : _egwAction . id == 'saveas' ? "save as" : "save" ,
value : "doc.odt"
} , this . et2 ) ;
// bind change handler for setting the selected path and calling save
jQuery ( vfs _select . getDOMNode ( ) ) . on ( 'change' , function ( ) {
2016-08-23 15:26:59 +02:00
file _path = vfs _select . get _value ( ) ;
2016-08-16 12:47:00 +02:00
if ( vfs _select . get _value ( ) )
{
// Add odt extension if not exist
if ( ! file _path . match ( /\.odt$/ , 'ig' ) ) file _path += '.odt' ;
widgetFilePath . set _value ( file _path ) ;
self . editor . getDocumentAsByteArray ( saveByteArrayLocally ) ;
2016-08-19 16:38:44 +02:00
self . editor _leaveSession ( function ( ) {
2016-08-23 15:26:59 +02:00
var path = window . location . href . split ( '&path=' ) ;
window . location . href = path [ 0 ] + '&path=' + self . editor _getFilePath ( ) ;
2016-08-19 16:38:44 +02:00
} ) ;
2016-08-16 12:47:00 +02:00
egw . refresh ( '' , 'filemanager' ) ;
}
} ) ;
// start the file selector dialog
jQuery ( vfs _select . getDOMNode ( ) ) . click ( ) ;
}
}
} ,
/ * *
* Method to delete loaded file in editor
* @ param { type } _egwAction
* /
editor _delete : function ( _egwAction ) {
var fullpath = this . et2 . getWidgetById ( 'file_path' ) . value ;
var selected = fullpath . split ( '/' ) ;
selected . pop ( ) ;
var path = selected . join ( '/' ) ;
var self = this ;
et2 _dialog . show _dialog (
function ( _btn )
{
if ( _btn == 2 )
{
2016-08-22 13:05:47 +02:00
self . _do _action ( 'delete' , [ fullpath , path + '/.' + self . collab _server . es _id + '.webodf.odt' ] , false , path ) ;
2016-08-16 12:47:00 +02:00
self . editor _close ( _egwAction ) ;
}
} ,
egw . lang ( 'Delete file %1?' , path ) ,
'Delete file' ,
null ,
et2 _dialog . BUTTONS _YES _NO ,
et2 _dialog . WARNING _MESSAGE
) ;
} ,
/ * *
* Function to handle file operations ( PGD ) for editor
*
* @ param { object } _params jquery ajax parameters
* /
editor _file _operation : function ( _params )
{
var ajaxObj = {
url : egw . webserverUrl + '/webdav.php?/home/' + egw . user ( 'account_lid' ) + '/default.odt'
} ;
jQuery . extend ( ajaxObj , _params ) ;
switch ( ajaxObj && ajaxObj . cmd )
{
case 'PUT' :
jQuery . extend ( { } , ajaxObj , {
data : JSON . stringify ( ajaxObj . data ) ,
contentType : 'application/json'
} ) ;
break ;
case 'GET' :
jQuery . extend ( { } , ajaxObj , {
dataType : 'json'
} ) ;
break ;
case 'DELETE' :
break ;
}
jQuery . ajax ( ajaxObj ) ;
} ,
/ * *
2016-08-19 16:38:44 +02:00
* Function to get full file path
2016-08-16 12:47:00 +02:00
*
2016-08-19 16:38:44 +02:00
* @ returns { String } retruns file path
2016-08-16 12:47:00 +02:00
* /
2016-08-19 16:38:44 +02:00
editor _getFilePath : function ( )
2016-08-16 12:47:00 +02:00
{
var widgetFilePath = this . et2 . getWidgetById ( 'file_path' ) ,
file _path = widgetFilePath . value ,
2016-08-23 15:26:59 +02:00
path = egw . webserverUrl + '/webdav.php' + file _path ;
2016-08-19 16:38:44 +02:00
return path ;
2016-08-16 12:47:00 +02:00
} ,
2016-08-22 17:46:48 +02:00
/ * *
* 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 }
2016-08-23 18:01:17 +02:00
] ;
2016-08-22 17:46:48 +02:00
et2 _dialog . show _dialog (
function ( _btn )
{
if ( _btn == 'save' )
{
self . editor _save ( { id : 'saveas' } ) ;
}
else if ( _btn == 'reload' )
{
2016-08-25 16:58:19 +02:00
self . collab _server . close = true ;
2016-08-22 17:46:48 +02:00
window . location . reload ( ) ;
}
} ,
2016-08-24 17:46:09 +02:00
egw . lang ( 'This session is not valid anymore! Save as your local changes if you need them or reload to join new session.' ) ,
2016-08-22 17:46:48 +02:00
'Delete file' ,
null ,
buttons ,
et2 _dialog . WARNING _MESSAGE
) ;
} ,
2016-08-16 12:47:00 +02:00
/ * *
* Function to create collab editor
*
* @ param { type } _args parameteres to be set for server factory and texteditor
* /
create _collab _editor : function ( _args )
{
var serverFactory ,
server ,
serverParams = _args . serverParams ,
sessionId = _args . sessionId ,
2016-08-23 12:29:49 +02:00
editorOptions = jQuery . extend ( _args . editorOptions , { } ) ,
2016-08-16 12:47:00 +02:00
userId = egw . user ( 'account_id' ) ,
memberId ,
self = this ;
/ * *
* Editor error handler function
2016-08-22 17:46:48 +02:00
*
* this function also been used in order to notify
* participant about session changes .
*
* @ param { string } e
2016-08-16 12:47:00 +02:00
* /
function handleEditingError ( e )
{
2016-08-22 17:46:48 +02:00
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 :
2016-08-23 18:01:17 +02:00
console . log ( e ) ;
2016-08-22 17:46:48 +02:00
}
2016-08-16 12:47:00 +02:00
} ;
function onEditing ( )
{
} ;
/ * *
* Callback function which gets called after the collab editor is created
*
* @ param { string } _err
* @ param { object } _editor webodf collabtexteditor object
*
* @ return { undefined } return undefined if something goes wrong
* /
function onEditorCreated ( _err , _editor )
{
if ( _err )
{
console . log ( 'Something went wrong whilst loading editor.' + _err ) ;
return ;
}
self . editor = _editor ;
2016-08-22 17:46:48 +02:00
self . editor . addEventListener ( Wodo . EVENT _UNKNOWNERROR , jQuery . proxy ( handleEditingError , self ) ) ;
2016-08-16 12:47:00 +02:00
self . editor . joinSession ( serverFactory . createSessionBackend ( sessionId , memberId , server ) , onEditing ) ;
} ;
/ * *
* Function to join a doc session
*
* @ param { type } _sessionId session id of the opened document
* /
function joinSession ( _sessionId )
{
var sid = _sessionId ;
2016-08-17 17:23:22 +02:00
2016-08-16 12:47:00 +02:00
server . joinSession ( userId , sid , function ( _memberId ) {
memberId = _memberId ;
2016-08-17 17:23:22 +02:00
// Set server object for current session
self . collab _server = { server : server , memberid : memberId , es _id : sid } ;
2016-08-16 12:47:00 +02:00
if ( Object . keys ( self . editor ) . length == 0 ) {
Wodo . createCollabTextEditor ( 'filemanager-editor_odfEditor' , editorOptions , onEditorCreated ) ;
} else {
self . editor . joinSession ( serverFactory . createSessionBackend ( sid , _memberId , server ) , onEditing ) ;
}
} , function ( err ) {
console . log ( err ) ;
} ) ;
} ;
2016-10-27 18:47:02 +02:00
webodfModule . require ( [ "egwCollab/ServerFactory" ] , function ( ServerFactory ) {
2016-08-16 12:47:00 +02:00
serverFactory = new ServerFactory ( ) ;
server = serverFactory . createServer ( serverParams ) ;
server . connect ( 8000 , function ( state ) {
switch ( state )
{
case "ready" :
joinSession ( sessionId ) ;
break ;
case "timeout" :
2016-08-23 18:01:17 +02:00
console . log ( 'did not connect to server because of timeout.' ) ;
2016-08-16 12:47:00 +02:00
break ;
default :
2016-08-23 18:01:17 +02:00
console . log ( 'server is not available.' ) ;
2016-08-16 12:47:00 +02:00
}
} ) ;
} ) ;
2016-08-22 13:05:47 +02:00
} ,
_do _action _callback : function ( _data )
{
this . _super . apply ( this , arguments ) ;
switch ( _data . action )
{
case 'delete' :
2016-08-25 16:25:26 +02:00
if ( ! _data . errs ) egw . json ( 'filemanager.filemanager_collab.ajax_actions' , [ { 'es_id' : this . collab _server . es _id } , 'delete' ] , function ( ) { window . close ( ) ; } ) . sendRequest ( ) ;
2016-08-22 13:05:47 +02:00
}
2016-08-22 17:46:48 +02:00
} ,
/ * *
* 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 }
2016-08-23 18:01:17 +02:00
] ;
2016-08-22 17:46:48 +02:00
et2 _dialog . show _dialog (
function ( _btn )
{
if ( _btn == 'discard' )
{
2016-08-25 16:25:26 +02:00
egw . json ( 'filemanager.filemanager_collab.ajax_actions' , [ { 'es_id' : self . collab _server . es _id } , 'discard' ] , function ( ) {
self . collab _server . close = true ;
2016-08-22 17:46:48 +02:00
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
) ;
2016-08-16 12:47:00 +02:00
}
} ) ;