2013-04-09 18:20:06 +02:00
/ * *
2013-04-13 14:44:50 +02:00
* EGroupware - Filemanager - Javascript UI
2013-04-09 18:20:06 +02:00
*
2021-06-10 11:38:54 +02:00
* @ link https : //www.egroupware.org
2013-04-09 18:20:06 +02:00
* @ package filemanager
* @ author Ralf Becker < RalfBecker - AT - outdoor - training . de >
2021-06-10 11:38:54 +02:00
* @ copyright ( c ) 2008 - 21 by Ralf Becker < RalfBecker - AT - outdoor - training . de >
2013-04-09 18:20:06 +02:00
* @ license http : //opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* /
2020-03-06 18:42:31 +01:00
/ * e g w : u s e s
/ a p i / j s / j s a p i / e g w _ a p p . j s ;
* /
2021-06-10 11:38:54 +02:00
import { EgwApp } from "../../api/js/jsapi/egw_app" ;
import { etemplate2 } from "../../api/js/etemplate/etemplate2" ;
import { et2 _dialog } from "../../api/js/etemplate/et2_widget_dialog" ;
import { et2 _file } from "../../api/js/etemplate/et2_widget_file" ;
import { et2 _button } from "../../api/js/etemplate/et2_widget_button" ;
import { et2 _nextmatch _controller } from "../../api/js/etemplate/et2_extension_nextmatch_controller" ;
import { egw _get _file _editor _prefered _mimes } from "../../api/js/jsapi/egw_global" ;
import { et2 _createWidget } from "../../api/js/etemplate/et2_core_widget" ;
2013-04-13 14:44:50 +02:00
/ * *
* UI for filemanager
* /
2021-06-10 11:38:54 +02:00
export class filemanagerAPP extends EgwApp {
2020-02-28 14:42:45 +01:00
/ * *
* Constructor
*
* @ memberOf app . filemanager
* /
2021-06-10 11:38:54 +02:00
constructor ( ) {
2020-03-23 17:05:46 +01:00
// call parent
2021-06-10 11:38:54 +02:00
super ( 'filemanager' ) ;
2020-02-28 14:42:45 +01:00
/ * *
* path widget , by template
* /
2021-06-10 11:38:54 +02:00
this . path _widget = { } ;
2020-02-28 14:42:45 +01:00
/ * *
* Are files cut into clipboard - need to be deleted at source on paste
* /
2021-06-10 11:38:54 +02:00
this . clipboard _is _cut = false ;
2020-02-28 14:42:45 +01:00
/ * *
* Regexp to convert id to a path , use this . id2path ( _id )
* /
2021-06-10 11:38:54 +02:00
this . remove _prefix = /^filemanager::/ ;
2020-02-28 14:42:45 +01:00
// Loading filemanager in its tab and home causes us problems with
// unwanted destruction, so we check for already existing path widgets
2021-06-10 11:38:54 +02:00
let lists = etemplate2 . getByApplication ( 'home' ) ;
for ( let i = 0 ; i < lists . length ; i ++ ) {
2020-02-28 14:42:45 +01:00
if ( lists [ i ] . app == 'filemanager' && lists [ i ] . widgetContainer . getWidgetById ( 'path' ) ) {
2021-06-10 11:38:54 +02:00
this . path _widget [ lists [ i ] . uniqueId ] = lists [ i ] . widgetContainer . getWidgetById ( 'path' ) ;
2020-02-28 14:42:45 +01:00
}
}
}
/ * *
* Destructor
* /
2021-06-10 11:38:54 +02:00
destroy ( _app ) {
2020-02-28 14:42:45 +01:00
delete this . et2 ;
// call parent
2021-06-10 11:38:54 +02:00
super . destroy ( _app ) ;
}
2020-02-28 14:42:45 +01:00
/ * *
* 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
* /
2021-06-10 11:38:54 +02:00
et2 _ready ( et2 , name ) {
2020-02-28 14:42:45 +01:00
// call parent
2021-06-10 11:38:54 +02:00
super . et2 _ready ( et2 , name ) ;
let path _widget = this . et2 . getWidgetById ( 'path' ) ;
2020-02-28 14:42:45 +01:00
if ( path _widget ) // do NOT set not found path-widgets, as uploads works on first one only!
{
this . path _widget [ et2 . DOMContainer . id ] = path _widget ;
// Bind to removal to remove from list
jQuery ( et2 . DOMContainer ) . on ( 'clear' , function ( e ) {
if ( app . filemanager && app . filemanager . path _widget )
delete app . filemanager . path _widget [ e . target . id ] ;
} ) ;
}
if ( this . et2 . getWidgetById ( 'nm' ) ) {
// Legacy JS only supports 2 arguments (event and widget), so set
// to the actual function here
this . et2 . getWidgetById ( 'nm' ) . set _onfiledrop ( jQuery . proxy ( this . filedrop , this ) ) ;
}
// get clipboard from browser localstore and update button tooltips
this . clipboard _tooltips ( ) ;
// calling set_readonly for initial path
if ( this . et2 . getArrayMgr ( 'content' ) . getEntry ( 'initial_path_readonly' ) ) {
this . readonly = [ this . et2 . getArrayMgr ( 'content' ) . getEntry ( 'nm[path]' ) , true ] ;
}
if ( typeof this . readonly != 'undefined' ) {
this . set _readonly . apply ( this , this . readonly ) ;
delete this . readonly ;
}
if ( name == 'filemanager.index' ) {
2021-06-10 11:38:54 +02:00
let fe = egw . link _get _registry ( 'filemanager-editor' ) ;
let new _widget = this . et2 . getWidgetById ( 'new' ) ;
2020-02-28 14:42:45 +01:00
if ( fe && fe [ "edit" ] ) {
2021-06-10 11:38:54 +02:00
let new _options = this . et2 . getArrayMgr ( 'sel_options' ) . getEntry ( 'new' ) ;
2020-02-28 14:42:45 +01:00
new _widget . set _select _options ( new _options ) ;
}
else if ( new _widget ) {
new _widget . set _disabled ( true ) ;
}
}
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Set the application ' s state to the given state .
*
* Extended from parent to also handle view
*
*
* @ param { { name : string , state : object } | string } state Object ( or JSON string ) for a state .
* Only state is required , and its contents are application specific .
*
* @ return { boolean } false - Returns false to stop event propagation
* /
2021-06-10 11:38:54 +02:00
setState ( state ) {
2020-02-28 14:42:45 +01:00
// State should be an object, not a string, but we'll parse
if ( typeof state == "string" ) {
if ( state . indexOf ( '{' ) != - 1 || state == 'null' ) {
state = JSON . parse ( state ) ;
}
}
2021-06-10 11:38:54 +02:00
let result = super . setState ( state , 'filemanager.index' ) ;
2020-02-28 14:42:45 +01:00
// This has to happen after the parent, changing to tile recreates
// nm controller
if ( typeof state == "object" && state . state && state . state . view ) {
2021-06-10 11:38:54 +02:00
let et2 = etemplate2 . getById ( 'filemanager-index' ) ;
2020-02-28 14:42:45 +01:00
if ( et2 ) {
this . et2 = et2 . widgetContainer ;
this . change _view ( state . state . view ) ;
}
}
return result ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Retrieve the current state of the application for future restoration
*
* Extended from parent to also set view
*
* @ return { object } Application specific map representing the current state
* /
2021-06-10 11:38:54 +02:00
getState ( ) {
let state = super . getState ( ) ;
let et2 = etemplate2 . getById ( 'filemanager-index' ) ;
2020-02-28 14:42:45 +01:00
if ( et2 ) {
2021-06-10 11:38:54 +02:00
let nm = et2 . widgetContainer . getWidgetById ( 'nm' ) ;
2020-02-28 14:42:45 +01:00
state . view = nm . view ;
}
return state ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Convert id to path ( remove "filemanager::" prefix )
* /
2021-06-10 11:38:54 +02:00
id2path ( _id ) {
2020-02-28 14:42:45 +01:00
return _id . replace ( this . remove _prefix , '' ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Convert array of elems to array of paths
* /
2021-06-10 11:38:54 +02:00
_elems2paths ( _elems ) {
let paths = [ ] ;
for ( let i = 0 ; i < _elems . length ; i ++ ) {
2020-02-28 14:42:45 +01:00
// If selected has no id, try parent. This happens for the placeholder row
// in empty directories.
paths . push ( _elems [ i ] . id ? this . id2path ( _elems [ i ] . id ) : _elems [ i ] . _context . _parentId ) ;
}
return paths ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Get directory of a path
* /
2021-06-10 11:38:54 +02:00
dirname ( _path ) {
let parts = _path . split ( '/' ) ;
2020-02-28 14:42:45 +01:00
parts . pop ( ) ;
return parts . join ( '/' ) || '/' ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Get name of a path
* /
2021-06-10 11:38:54 +02:00
basename ( _path ) {
2020-02-28 14:42:45 +01:00
return _path . split ( '/' ) . pop ( ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Get current working directory
* /
2021-06-10 11:38:54 +02:00
get _path ( etemplate _name ) {
2020-02-28 14:42:45 +01:00
if ( ! etemplate _name || typeof this . path _widget [ etemplate _name ] == 'undefined' ) {
for ( etemplate _name in this . path _widget )
break ;
}
2021-06-10 11:38:54 +02:00
let path _widget = this . path _widget [ etemplate _name ] ;
2020-02-28 14:42:45 +01:00
return path _widget ? path _widget . get _value . apply ( path _widget ) : null ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Open compose with already attached files
*
* @ param { ( string | string [ ] ) } attachments path ( s )
* @ param { object } params
* /
2021-06-10 11:38:54 +02:00
open _mail ( attachments , params ) {
2020-02-28 14:42:45 +01:00
if ( typeof attachments == 'undefined' )
attachments = this . get _clipboard _files ( ) ;
if ( ! params || typeof params != 'object' )
params = { } ;
if ( ! ( attachments instanceof Array ) )
attachments = [ attachments ] ;
2021-06-10 11:38:54 +02:00
let content = { data : { files : { file : [ ] } } } ;
for ( let i = 0 ; i < attachments . length ; i ++ ) {
2020-02-28 14:42:45 +01:00
params [ 'preset[file][' + i + ']' ] = 'vfs://default' + attachments [ i ] ;
content . data . files . file . push ( 'vfs://default' + attachments [ i ] ) ;
}
content . data . files [ "filemode" ] = params [ 'preset[filemode]' ] ;
// always open compose in html mode, as attachment links look a lot nicer in html
params [ "mimeType" ] = 'html' ;
2021-05-17 16:27:51 +02:00
return egw . openWithinWindow ( "mail" , "setCompose" , content , params , /mail.mail_compose.compose/ , true ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Mail files action : open compose with already attached files
*
* @ param _action
* @ param _elems
* /
2021-06-10 11:38:54 +02:00
mail ( _action , _elems ) {
2020-02-28 14:42:45 +01:00
this . open _mail ( this . _elems2paths ( _elems ) , {
'preset[filemode]' : _action . id . substr ( 5 )
} ) ;
2021-06-10 11:38:54 +02:00
}
2020-03-26 20:20:20 +01:00
/ * *
* Mail files action : open compose with already linked files
* We ' re only interested in hidden upload shares here , open _mail can handle
* the rest
*
* @ param { egwAction } _action
* @ param { egwActionObject [ ] } _selected
* /
2021-06-10 11:38:54 +02:00
mail _share _link ( _action , _selected ) {
2020-03-26 20:20:20 +01:00
if ( _action . id !== 'mail_shareUploadDir' ) {
return this . mail ( _action , _selected ) ;
}
2021-06-10 11:38:54 +02:00
let path = this . id2path ( _selected [ 0 ] . id ) ;
2020-03-26 20:20:20 +01:00
this . share _link ( _action , _selected , null , false , false , this . _mail _link _callback ) ;
return true ;
2021-06-10 11:38:54 +02:00
}
2020-03-26 20:20:20 +01:00
/ * *
* Callback with the share link to append to an email
*
* @ param { Object } _data
* @ param { String } _data . share _link Link to the share
* @ param { String } _data . title Title for the link
* @ param { String } [ _data . msg ] Error message
* /
2021-06-10 11:38:54 +02:00
_mail _link _callback ( _data ) {
2020-03-26 20:20:20 +01:00
debugger ;
if ( _data . msg || ! _data . share _link )
window . egw _refresh ( _data . msg , this . appname ) ;
2021-06-10 11:38:54 +02:00
let params = {
2020-03-26 20:20:20 +01:00
'preset[body]' : '<a href="' + _data . share _link + '">' + _data . title + '</a>' ,
'mimeType' : 'html' // always open compose in html mode, as attachment links look a lot nicer in html
} ;
2021-06-10 11:38:54 +02:00
let content = {
2020-03-26 20:20:20 +01:00
mail _htmltext : [ '<br /><a href="' + _data . share _link + '">' + _data . title + '</a>' ] ,
mail _plaintext : [ "\n" + _data . share _link ]
} ;
return egw . openWithinWindow ( "mail" , "setCompose" , content , params , /mail.mail_compose.compose/ ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Trigger Upload after each file is uploaded
* @ param { type } _event
* /
2021-06-10 11:38:54 +02:00
uploadOnOne ( _event ) {
2020-02-28 14:42:45 +01:00
this . upload ( _event , 1 ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Send names of uploaded files ( again ) to server , to process them : either copy to vfs or ask overwrite / rename
*
* @ param { event } _event
* @ param { number } _file _count
* @ param { string = } _path where the file is uploaded to , default current directory
2020-03-25 03:34:04 +01:00
* @ param { string } _conflict What to do if the file conflicts with one on the server
2020-03-27 20:40:48 +01:00
* @ param { string } _target Upload processing target . Sharing classes can override this .
2020-02-28 14:42:45 +01:00
* /
2021-06-10 11:38:54 +02:00
upload ( _event , _file _count , _path , _conflict = "ask" , _target = 'filemanager_ui::ajax_action' ) {
2020-02-28 14:42:45 +01:00
if ( typeof _path == 'undefined' ) {
_path = this . get _path ( ) ;
}
if ( _file _count && ! jQuery . isEmptyObject ( _event . data . getValue ( ) ) ) {
2021-06-10 11:38:54 +02:00
let widget = _event . data ;
let value = widget . getValue ( ) ;
2020-03-25 03:34:04 +01:00
value . conflict = _conflict ;
2021-04-28 22:19:51 +02:00
egw . json ( _target , [ 'upload' , value , _path , { ui _path : this . egw . window . location . pathname } ] , this . _upload _callback , this , true , this ) . sendRequest ( ) ;
2020-02-28 14:42:45 +01:00
widget . set _value ( '' ) ;
}
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Finish callback for file a file dialog , to get the overwrite / rename prompt
*
* @ param { event } _event
* @ param { number } _file _count
* /
2021-06-10 11:38:54 +02:00
file _a _file _upload ( _event , _file _count ) {
let widget = _event . data ;
let path = widget . getRoot ( ) . getWidgetById ( "path" ) . getValue ( ) ;
let action = widget . getRoot ( ) . getWidgetById ( "action" ) . getValue ( ) ;
let link = widget . getRoot ( ) . getWidgetById ( "entry" ) . getValue ( ) ;
2020-02-28 14:42:45 +01:00
if ( action == 'save_as' && link . app && link . id ) {
path = "/apps/" + link . app + "/" + link . id ;
}
2021-06-10 11:38:54 +02:00
let props = widget . getInstanceManager ( ) . getValues ( widget . getRoot ( ) ) ;
2020-02-28 14:42:45 +01:00
egw . json ( 'filemanager_ui::ajax_action' , [ action == 'save_as' ? 'upload' : 'link' , widget . getValue ( ) , path , props ] , function ( _data ) {
app . filemanager . _upload _callback ( _data ) ;
// Remove successful after a delay
for ( var file in _data . uploaded ) {
if ( ! _data . uploaded [ file ] . confirm || _data . uploaded [ file ] . confirmed ) {
// Remove that file from file widget...
widget . remove _file ( _data . uploaded [ file ] . name ) ;
}
}
opener . egw _refresh ( '' , 'filemanager' , null , null , 'filemanager' ) ;
} , app . filemanager , true , this ) . sendRequest ( true ) ;
return true ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Callback for server response to upload request :
* - display message and refresh list
* - ask use to confirm overwritting existing files or rename upload
*
* @ param { object } _data values for attributes msg , files , ...
* /
2021-06-10 11:38:54 +02:00
_upload _callback ( _data ) {
2020-02-28 14:42:45 +01:00
if ( _data . msg || _data . uploaded )
2020-03-27 20:40:48 +01:00
window . egw _refresh ( _data . msg , this . appname , undefined , undefined , undefined , undefined , undefined , _data . type ) ;
2021-06-10 11:38:54 +02:00
let that = this ;
for ( let file in _data . uploaded ) {
2020-02-28 14:42:45 +01:00
if ( _data . uploaded [ file ] . confirm && ! _data . uploaded [ file ] . confirmed ) {
2021-06-10 11:38:54 +02:00
let buttons = [
2020-03-23 17:05:46 +01:00
{
text : this . egw . lang ( "Yes" ) ,
id : "overwrite" ,
class : "ui-priority-primary" ,
"default" : true ,
image : 'check'
} ,
2020-02-28 14:42:45 +01:00
{ text : this . egw . lang ( "Rename" ) , id : "rename" , image : 'edit' } ,
{ text : this . egw . lang ( "Cancel" ) , id : "cancel" }
] ;
if ( _data . uploaded [ file ] . confirm === "is_dir" )
buttons . shift ( ) ;
2021-06-10 11:38:54 +02:00
let dialog = et2 _dialog . show _prompt ( function ( _button _id , _value ) {
let uploaded = { } ;
2020-02-28 14:42:45 +01:00
uploaded [ this . my _data . file ] = this . my _data . data ;
switch ( _button _id ) {
case "overwrite" :
uploaded [ this . my _data . file ] . confirmed = true ;
// fall through
case "rename" :
uploaded [ this . my _data . file ] . name = _value ;
delete uploaded [ this . my _data . file ] . confirm ;
// send overwrite-confirmation and/or rename request to server
egw . json ( 'filemanager_ui::ajax_action' , [ this . my _data . action , uploaded , this . my _data . path , this . my _data . props ] , that . _upload _callback , that , true , that ) . sendRequest ( ) ;
return ;
case "cancel" :
// Remove that file from every file widget...
that . et2 . iterateOver ( function ( _widget ) {
_widget . remove _file ( this . my _data . data . name ) ;
} , this , et2 _file ) ;
}
} , _data . uploaded [ file ] . confirm === "is_dir" ?
this . egw . lang ( "There's already a directory with that name!" ) :
this . egw . lang ( 'Do you want to overwrite existing file %1 in directory %2?' , _data . uploaded [ file ] . name , _data . path ) , this . egw . lang ( 'File %1 already exists' , _data . uploaded [ file ] . name ) , _data . uploaded [ file ] . name , buttons , file ) ;
// setting required data for callback in as my_data
dialog . my _data = {
action : _data . action ,
file : file ,
path : _data . path ,
data : _data . uploaded [ file ] ,
props : _data . props
} ;
}
}
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Get any files that are in the system clipboard
*
* @ return { string [ ] } Paths
* /
2021-06-10 11:38:54 +02:00
get _clipboard _files ( ) {
let clipboard _files = [ ] ;
2020-02-28 14:42:45 +01:00
if ( typeof window . localStorage != 'undefined' && typeof egw . getSessionItem ( 'phpgwapi' , 'egw_clipboard' ) != 'undefined' ) {
2021-06-10 11:38:54 +02:00
let clipboard = JSON . parse ( egw . getSessionItem ( 'phpgwapi' , 'egw_clipboard' ) ) || {
2020-02-28 14:42:45 +01:00
type : [ ] ,
selected : [ ]
} ;
if ( clipboard . type . indexOf ( 'file' ) >= 0 ) {
2021-06-10 11:38:54 +02:00
for ( let i = 0 ; i < clipboard . selected . length ; i ++ ) {
let split = clipboard . selected [ i ] . id . split ( '::' ) ;
2020-02-28 14:42:45 +01:00
if ( split [ 0 ] == 'filemanager' ) {
clipboard _files . push ( this . id2path ( clipboard . selected [ i ] . id ) ) ;
}
}
}
}
return clipboard _files ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Update clickboard tooltips in buttons
* /
2021-06-10 11:38:54 +02:00
clipboard _tooltips ( ) {
let paste _buttons = [ 'button[paste]' , 'button[linkpaste]' , 'button[mailpaste]' ] ;
for ( let i = 0 ; i < paste _buttons . length ; ++ i ) {
let button = this . et2 . getWidgetById ( paste _buttons [ i ] ) ;
2020-02-28 14:42:45 +01:00
if ( button )
button . set _statustext ( this . get _clipboard _files ( ) . join ( ",\n" ) ) ;
}
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Clip files into clipboard
*
* @ param _action
* @ param _elems
* /
2021-06-10 11:38:54 +02:00
clipboard ( _action , _elems ) {
2020-02-28 14:42:45 +01:00
this . clipboard _is _cut = _action . id == "cut" ;
2021-06-10 11:38:54 +02:00
let clipboard = JSON . parse ( egw . getSessionItem ( 'phpgwapi' , 'egw_clipboard' ) ) || {
2020-02-28 14:42:45 +01:00
type : [ ] ,
selected : [ ]
} ;
if ( _action . id != "add" ) {
clipboard = {
type : [ ] ,
selected : [ ]
} ;
}
// When pasting we need to know the type of data - pull from actions
2021-06-10 11:38:54 +02:00
let drag = _elems [ 0 ] . getSelectedLinks ( 'drag' ) . links ;
for ( let k in drag ) {
2020-02-28 14:42:45 +01:00
if ( drag [ k ] . enabled && drag [ k ] . actionObj . dragType . length > 0 ) {
clipboard . type = clipboard . type . concat ( drag [ k ] . actionObj . dragType ) ;
}
}
clipboard . type = jQuery . unique ( clipboard . type ) ;
// egwAction is a circular structure and can't be stringified so just take what we want
// Hopefully that's enough for the action handlers
2021-06-10 11:38:54 +02:00
for ( let k in _elems ) {
2020-02-28 14:42:45 +01:00
if ( _elems [ k ] . id )
clipboard . selected . push ( { id : _elems [ k ] . id , data : _elems [ k ] . data } ) ;
}
// Save it in session
egw . setSessionItem ( 'phpgwapi' , 'egw_clipboard' , JSON . stringify ( clipboard ) ) ;
this . clipboard _tooltips ( ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Paste files into current directory or mail them
*
* @ param _type 'paste' , 'linkpaste' , 'mailpaste'
* /
2021-06-10 11:38:54 +02:00
paste ( _type ) {
let clipboard _files = this . get _clipboard _files ( ) ;
2020-02-28 14:42:45 +01:00
if ( clipboard _files . length == 0 ) {
alert ( this . egw . lang ( 'Clipboard is empty!' ) ) ;
return ;
}
switch ( _type ) {
case 'mailpaste' :
this . open _mail ( clipboard _files ) ;
break ;
case 'paste' :
this . _do _action ( this . clipboard _is _cut ? 'move' : 'copy' , clipboard _files ) ;
if ( this . clipboard _is _cut ) {
this . clipboard _is _cut = false ;
clipboard _files = [ ] ;
this . clipboard _tooltips ( ) ;
}
break ;
case 'linkpaste' :
this . _do _action ( 'symlink' , clipboard _files ) ;
break ;
}
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Pass action to server
*
* @ param _action
* @ param _elems
* /
2021-06-10 11:38:54 +02:00
action ( _action , _elems ) {
let paths = this . _elems2paths ( _elems ) ;
let path = this . get _path ( _action && _action . parent . data . nextmatch . getInstanceManager ( ) . uniqueId || false ) ;
2020-02-28 14:42:45 +01:00
this . _do _action ( _action . id , paths , true , path ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Prompt user for directory to create
*
* @ param { egwAction | undefined } action Action , event or undefined if called directly
* @ param { egwActionObject [ ] | undefined } selected Selected row , or undefined if called directly
* /
2021-06-10 11:38:54 +02:00
createdir ( action , selected ) {
let self = this ;
2020-02-28 14:42:45 +01:00
et2 _dialog . show _prompt ( function ( button , dir ) {
if ( button && dir ) {
2021-06-10 11:38:54 +02:00
let path = self . get _path ( action && action . parent ? action . parent . data . nextmatch . getInstanceManager ( ) . uniqueId : false ) ;
2020-02-28 14:42:45 +01:00
if ( action && action instanceof egwAction ) {
2021-06-10 11:38:54 +02:00
let paths = self . _elems2paths ( selected ) ;
2020-02-28 14:42:45 +01:00
if ( paths [ 0 ] )
path = paths [ 0 ] ;
// check if target is a file --> use it's directory instead
if ( selected [ 0 ] . id || path ) {
2021-06-10 11:38:54 +02:00
let data = egw . dataGetUIDdata ( selected [ 0 ] . id || 'filemanager::' + path ) ;
2020-02-28 14:42:45 +01:00
if ( data && data . data . mime != 'httpd/unix-directory' ) {
path = self . dirname ( path ) ;
}
}
}
self . _do _action ( 'createdir' , egw . encodePathComponent ( dir ) , true , path ) ; // true=synchronous request
self . change _dir ( ( path == '/' ? '' : path ) + '/' + egw . encodePathComponent ( dir ) ) ;
}
} , this . egw . lang ( 'New directory' ) , this . egw . lang ( 'Create directory' ) ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Prompt user for directory to create
* /
2021-06-10 11:38:54 +02:00
symlink ( ) {
let self = this ;
2020-02-28 14:42:45 +01:00
et2 _dialog . show _prompt ( function ( button , target ) {
if ( button && target ) {
self . _do _action ( 'symlink' , target ) ;
}
} , this . egw . lang ( 'Link target' ) , this . egw . lang ( 'Create link' ) ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Run a serverside action via an ajax call
*
* @ param _type 'move_file' , 'copy_file' , ...
* @ param _selected selected paths
* @ param _sync send a synchronous ajax request
* @ param _path defaults to current path
* /
2021-06-10 11:38:54 +02:00
_do _action ( _type , _selected , _sync , _path ) {
2020-02-28 14:42:45 +01:00
if ( typeof _path == 'undefined' )
_path = this . get _path ( ) ;
egw . json ( 'filemanager_ui::ajax_action' , [ _type , _selected , _path ] , this . _do _action _callback , this , ! _sync , this ) . sendRequest ( ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Callback for _do _action ajax call
*
* @ param _data
* /
2021-06-10 11:38:54 +02:00
_do _action _callback ( _data ) {
2020-03-27 20:40:48 +01:00
window . egw _refresh ( _data . msg , this . appname , undefined , undefined , undefined , undefined , undefined , _data . type ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Force download of a file by appending '?download' to it ' s download url
*
* @ param _action
* @ param _senders
* /
2021-06-10 11:38:54 +02:00
force _download ( _action , _senders ) {
for ( let i = 0 ; i < _senders . length ; i ++ ) {
let data = egw . dataGetUIDdata ( _senders [ i ] . id ) ;
let url = data ? data . data . download _url : '/webdav.php' + this . id2path ( _senders [ i ] . id ) ;
2020-02-28 14:42:45 +01:00
if ( url [ 0 ] == '/' )
url = egw . link ( url ) ;
2021-06-10 11:38:54 +02:00
let a = document . createElement ( 'a' ) ;
2020-02-28 14:42:45 +01:00
if ( typeof a . download == "undefined" ) {
window . location = ( url + "?download" ) ;
return false ;
}
// Multiple file download for those that support it
2021-06-10 11:38:54 +02:00
let $a = jQuery ( a )
2020-02-28 14:42:45 +01:00
. prop ( 'href' , url )
. prop ( 'download' , data ? data . data . name : "" )
. appendTo ( this . et2 . getDOMNode ( ) ) ;
window . setTimeout ( jQuery . proxy ( function ( ) {
2021-06-10 11:38:54 +02:00
let evt = document . createEvent ( 'MouseEvent' ) ;
2020-02-28 14:42:45 +01:00
evt . initMouseEvent ( 'click' , true , true , window , 1 , 0 , 0 , 0 , 0 , false , false , false , false , 0 , null ) ;
this [ 0 ] . dispatchEvent ( evt ) ;
this . remove ( ) ;
} , $a ) , 100 * i ) ;
}
return false ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Check to see if the browser supports downloading multiple files
* ( using a tag download attribute ) to enable / disable the context menu
*
* @ param { egwAction } action
* @ param { egwActionObject [ ] } selected
* /
2021-06-10 11:38:54 +02:00
is _multiple _allowed ( action , selected ) {
let allowed = typeof document . createElement ( 'a' ) . download != "undefined" ;
2020-02-28 14:42:45 +01:00
if ( typeof action == "undefined" )
return allowed ;
return ( allowed || selected . length <= 1 ) && action . not _disableClass . apply ( action , arguments ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Change directory
*
* @ param { string } _dir directory to change to incl . '..' for one up
* @ param { et2 _widget } widget
* /
2021-06-10 11:38:54 +02:00
change _dir ( _dir , widget ) {
2020-02-28 14:42:45 +01:00
for ( var etemplate _name in this . path _widget )
break ;
if ( widget )
etemplate _name = widget . getInstanceManager ( ) . uniqueId ;
// Make sure everything is in place for changing directory
if ( ! this . et2 || typeof etemplate _name !== 'string' ||
typeof this . path _widget [ etemplate _name ] === 'undefined' ) {
return false ;
}
switch ( _dir ) {
case '..' :
_dir = this . dirname ( this . get _path ( etemplate _name ) ) ;
break ;
case '~' :
_dir = this . et2 . getWidgetById ( 'nm' ) . options . settings . home _dir ;
break ;
}
this . path _widget [ etemplate _name ] . set _value ( _dir ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Toggle view between tiles and rows
*
* @ param { string | Event } [ view ] - Specify what to change the view to . Either 'tile' or 'row' .
* Or , if this is used as a callback view is actually the event , and we need to find the view .
* @ param { et2 _widget } [ button _widget ] - The widget that ' s calling
* /
2021-06-10 11:38:54 +02:00
change _view ( view , button _widget ) {
let et2 = etemplate2 . getById ( 'filemanager-index' ) ;
let nm ;
2020-02-28 14:42:45 +01:00
if ( et2 && et2 . widgetContainer . getWidgetById ( 'nm' ) ) {
nm = et2 . widgetContainer . getWidgetById ( 'nm' ) ;
}
if ( ! nm ) {
egw . debug ( 'warn' , 'Could not find nextmatch to change view' ) ;
return ;
}
if ( ! button _widget ) {
button _widget = nm . getWidgetById ( 'button[change_view]' ) ;
}
if ( button _widget && button _widget . instanceOf ( et2 _button ) ) {
// Switch view based on button icon, since controller can get re-created
if ( typeof view != 'string' ) {
view = button _widget . options . image . replace ( 'list_' , '' ) ;
}
// Toggle button icon to the other view
//todo: nm.controller needs to be changed to nm.getController after merging typescript branch into master
2020-03-23 17:05:46 +01:00
button _widget . set _image ( "list_" + ( view == et2 _nextmatch _controller . VIEW _ROW ? et2 _nextmatch _controller . VIEW _TILE : et2 _nextmatch _controller . VIEW _ROW ) ) ;
button _widget . set _statustext ( view == et2 _nextmatch _controller . VIEW _ROW ? this . egw . lang ( "Tile view" ) : this . egw . lang ( 'List view' ) ) ;
2020-02-28 14:42:45 +01:00
}
nm . set _view ( view ) ;
// Put it into active filters (but don't refresh)
nm . activeFilters [ "view" ] = view ;
// Change template to match
2021-06-10 11:38:54 +02:00
let template = view == et2 _nextmatch _controller . VIEW _ROW ? 'filemanager.index.rows' : 'filemanager.tile' ;
2020-02-28 14:42:45 +01:00
nm . set _template ( template ) ;
// Wait for template to load, then refresh
template = nm . getWidgetById ( template ) ;
if ( template && template . loading ) {
template . loading . done ( function ( ) {
nm . applyFilters ( { view : view } ) ;
} ) ;
}
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Open / active an item
*
* @ param _action
* @ param _senders
* /
2021-06-10 11:38:54 +02:00
open ( _action , _senders ) {
let data = egw . dataGetUIDdata ( _senders [ 0 ] . id ) ;
let path = this . id2path ( _senders [ 0 ] . id ) ;
2020-02-28 14:42:45 +01:00
this . et2 = this . et2 ? this . et2 : etemplate2 . getById ( 'filemanager-index' ) . widgetContainer ;
2021-06-10 11:38:54 +02:00
let mime = this . et2 . _inst . widgetContainer . getWidgetById ( '$row' ) ;
2020-02-28 14:42:45 +01:00
// try to get mime widget DOM node out of the row DOM
2021-06-10 11:38:54 +02:00
let mime _dom = jQuery ( _senders [ 0 ] . iface . getDOMNode ( ) ) . find ( "span#filemanager-index_\\$row" ) ;
let fe = egw _get _file _editor _prefered _mimes ( ) ;
2020-02-28 14:42:45 +01:00
// symlinks dont have mime 'http/unix-directory', but server marks all directories with class 'isDir'
if ( data . data . mime == 'httpd/unix-directory' || data . data [ 'class' ] && data . data [ 'class' ] . split ( / +/ ) . indexOf ( 'isDir' ) != - 1 ) {
this . change _dir ( path , _action . parent . data . nextmatch || this . et2 ) ;
}
else if ( mime && data . data . mime . match ( mime . mime _regexp ) && mime _dom . length > 0 ) {
mime _dom . click ( ) ;
}
else if ( mime && this . isEditable ( _action , _senders ) && fe && fe . edit ) {
egw . open _link ( egw . link ( '/index.php' , {
menuaction : fe . edit . menuaction ,
path : decodeURIComponent ( data . data . download _url )
} ) , '' , fe . edit _popup ) ;
}
else {
2021-06-10 11:38:54 +02:00
let url ;
2020-02-28 14:42:45 +01:00
// Build ViewerJS url
if ( data . data . mime . match ( /application\/vnd\.oasis\.opendocument/ ) &&
egw . preference ( 'document_doubleclick_action' , 'filemanager' ) == 'collabeditor' ) {
url = '/ViewerJS/#..' + data . data . download _url ;
}
egw . open ( { path : path , type : data . data . mime , download _url : url } , 'file' , 'view' , null , '_browser' ) ;
}
return false ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Edit prefs of current directory
*
* @ param _action
* @ param _senders
* /
2021-06-10 11:38:54 +02:00
editprefs ( _action , _senders ) {
let path = typeof _senders != 'undefined' ? this . id2path ( _senders [ 0 ] . id ) : this . get _path ( _action && _action . parent . data . nextmatch . getInstanceManager ( ) . uniqueId || false ) ;
2020-02-28 14:42:45 +01:00
egw ( ) . open _link ( egw . link ( '/index.php' , {
menuaction : 'filemanager.filemanager_ui.file' ,
path : path
} ) , 'fileprefs' , '510x425' ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Callback to check if the paste action is enabled . We also update the
* clipboard historical targets here as well
*
* @ param { egwAction } _action drop action we ' re checking
* @ param { egwActionObject [ ] } _senders selected files
* @ param { egwActionObject } _target Drop or context menu activated on this one
*
* @ returns boolean true if enabled , false otherwise
* /
2021-06-10 11:38:54 +02:00
paste _enabled ( _action , _senders , _target ) {
2020-02-28 14:42:45 +01:00
// Need files in the clipboard for this
2021-06-10 11:38:54 +02:00
let clipboard _files = this . get _clipboard _files ( ) ;
2020-02-28 14:42:45 +01:00
if ( clipboard _files . length === 0 ) {
return false ;
}
// Parent action (paste) gets run through here as well, but needs no
// further processing
if ( _action . id == 'paste' )
return true ;
if ( _action . canHaveChildren . indexOf ( 'drop' ) == - 1 ) {
_action . canHaveChildren . push ( 'drop' ) ;
}
2021-06-10 11:38:54 +02:00
let actions = [ ] ;
2020-02-28 14:42:45 +01:00
// Current directory
2021-06-10 11:38:54 +02:00
let current _dir = this . get _path ( ) ;
let dir = egw . dataGetUIDdata ( 'filemanager::' + current _dir ) ;
let path _widget = etemplate2 . getById ( 'filemanager-index' ) . widgetContainer . getWidgetById ( 'button[createdir]' ) ;
2020-02-28 14:42:45 +01:00
actions . push ( {
id : _action . id + '_current' , caption : current _dir , path : current _dir ,
enabled : dir && dir . data && dir . data . class && dir . data . class . indexOf ( 'noEdit' ) === - 1 ||
! dir && path _widget && ! path _widget . options . readonly
} ) ;
// Target, if directory
2021-06-10 11:38:54 +02:00
let target _dir = this . id2path ( _target . id ) ;
2020-02-28 14:42:45 +01:00
dir = egw . dataGetUIDdata ( _target . id ) ;
actions . push ( {
id : _action . id + '_target' ,
caption : target _dir ,
path : target _dir ,
enabled : _target && _target . iface && jQuery ( _target . iface . getDOMNode ( ) ) . hasClass ( 'isDir' ) &&
( dir && dir . data && dir . data . class && dir . data . class . indexOf ( 'noEdit' ) === - 1 || ! dir )
} ) ;
// Last 10 folders
2021-06-10 11:38:54 +02:00
let previous _dsts = jQuery . extend ( [ ] , egw . preference ( 'drop_history' , this . appname ) ) ;
let action _index = 0 ;
for ( let i = 0 ; i < 10 ; i ++ ) {
let path = i < previous _dsts . length ? previous _dsts [ i ] : '' ;
2020-02-28 14:42:45 +01:00
actions . push ( {
id : _action . id + '_target_' + action _index ++ ,
caption : path ,
path : path ,
group : 2 ,
enabled : path && ! ( current _dir && path === current _dir || target _dir && path === target _dir )
} ) ;
}
// Common stuff, every action needs these
2021-06-10 11:38:54 +02:00
for ( let i = 0 ; i < actions . length ; i ++ ) {
2020-02-28 14:42:45 +01:00
//actions[i].type = 'drop',
actions [ i ] . acceptedTypes = _action . acceptedTypes ;
actions [ i ] . no _lang = true ;
actions [ i ] . hideOnDisabled = true ;
}
_action . updateActions ( actions ) ;
// Create paste action
// This injects the clipboard data and calls the original handler
2021-06-10 11:38:54 +02:00
let paste _exec = function ( action , selected ) {
2020-02-28 14:42:45 +01:00
// Add in clipboard as a sender
2021-06-10 11:38:54 +02:00
let clipboard = JSON . parse ( egw . getSessionItem ( 'phpgwapi' , 'egw_clipboard' ) ) ;
2020-02-28 14:42:45 +01:00
// Set a flag so apps can tell the difference, if they need to
action . set _onExecute ( action . parent . onExecute . fnct ) ;
action . execute ( clipboard . selected , selected [ 0 ] ) ;
// Clear the clipboard, the files are not there anymore
if ( action . id . indexOf ( 'move' ) !== - 1 ) {
egw . setSessionItem ( 'phpgwapi' , 'egw_clipboard' , JSON . stringify ( {
type : [ ] ,
selected : [ ]
} ) ) ;
}
} ;
2021-06-10 11:38:54 +02:00
for ( let i = 0 ; i < actions . length ; i ++ ) {
2020-02-28 14:42:45 +01:00
_action . getActionById ( actions [ i ] . id ) . onExecute = jQuery . extend ( true , { } , _action . onExecute ) ;
_action . getActionById ( actions [ i ] . id ) . set _onExecute ( paste _exec ) ;
}
return actions . length > 0 ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* File ( s ) droped
*
* @ param _action
* @ param _elems
* @ param _target
* @ returns
* /
2021-06-10 11:38:54 +02:00
drop ( _action , _elems , _target ) {
let src = this . _elems2paths ( _elems ) ;
2020-02-28 14:42:45 +01:00
// Target will be missing ID if directory is empty
// so start with the current directory
2021-06-10 11:38:54 +02:00
let parent = _action ;
let nm = _target ? _target . manager . data . nextmatch : null ;
2020-02-28 14:42:45 +01:00
while ( ! nm && parent . parent ) {
parent = parent . parent ;
if ( parent . data . nextmatch )
nm = parent . data . nextmatch ;
}
2021-06-10 11:38:54 +02:00
let nm _dst = this . get _path ( nm . getInstanceManager ( ) . uniqueId || false ) ;
let dst ;
2020-02-28 14:42:45 +01:00
// Action specifies a destination, target does not matter
if ( _action . data && _action . data . path ) {
dst = _action . data . path ;
}
// File(s) were dropped on a row, they want them inside
else if ( _target ) {
dst = '' ;
2021-06-10 11:38:54 +02:00
let paths = this . _elems2paths ( [ _target ] ) ;
2020-02-28 14:42:45 +01:00
if ( paths [ 0 ] )
dst = paths [ 0 ] ;
// check if target is a file --> use it's directory instead
if ( _target . id ) {
2021-06-10 11:38:54 +02:00
let data = egw . dataGetUIDdata ( _target . id ) ;
2020-02-28 14:42:45 +01:00
if ( ! data || data . data . mime != 'httpd/unix-directory' ) {
dst = this . dirname ( dst ) ;
}
}
}
// Remember the target for next time
2021-06-10 11:38:54 +02:00
let previous _dsts = jQuery . extend ( [ ] , egw . preference ( 'drop_history' , this . appname ) ) ;
2020-02-28 14:42:45 +01:00
previous _dsts . unshift ( dst ) ;
2020-03-23 17:05:46 +01:00
previous _dsts = Array . from ( new Set ( previous _dsts ) ) . slice ( 0 , 9 ) ;
egw . set _preference ( this . appname , 'drop_history' , previous _dsts ) ;
2020-02-28 14:42:45 +01:00
// Actual action id will be something like file_drop_{move|copy|link}[_other_id],
// but we need to send move, copy or link
2021-06-10 11:38:54 +02:00
let action _id = _action . id . replace ( "file_drop_" , '' ) . split ( '_' , 1 ) [ 0 ] ;
2020-02-28 14:42:45 +01:00
this . _do _action ( action _id , src , false , dst || nm _dst ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Handle a native / HTML5 file drop from system
*
* This is a callback from nextmatch to prevent the default link action , and just upload instead .
*
* @ param { string } row _uid UID of the row the files were dropped on
* @ param { Files [ ] } files
* /
2021-06-10 11:38:54 +02:00
filedrop ( row _uid , files ) {
let self = this ;
let data = egw . dataGetUIDdata ( row _uid ) ;
2020-02-28 14:42:45 +01:00
files = files || window . event . dataTransfer . files ;
2021-06-10 11:38:54 +02:00
let path = typeof data != 'undefined' && data . data . mime == "httpd/unix-directory" ? data . data . path : this . get _path ( ) ;
let widget = this . et2 . getWidgetById ( 'upload' ) ;
2020-02-28 14:42:45 +01:00
// Override finish to specify a potentially different path
2021-06-10 11:38:54 +02:00
let old _onfinishone = widget . options . onFinishOne ;
let old _onfinish = widget . options . onFinish ;
2020-02-28 14:42:45 +01:00
widget . options . onFinishOne = function ( _event , _file _count ) {
self . upload ( _event , _file _count , path ) ;
} ;
widget . options . onFinish = function ( ) {
widget . options . onFinish = old _onfinish ;
widget . options . onFinishOne = old _onfinishone ;
} ;
// This triggers the upload
widget . set _value ( files ) ;
// Return false to prevent the link
return false ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Change readonly state for given directory
*
* Get call / transported with each get _rows call , but should only by applied to UI if matching curent dir
*
* @ param { string } _path
* @ param { boolean } _ro
* /
2021-06-10 11:38:54 +02:00
set _readonly ( _path , _ro ) {
2020-02-28 14:42:45 +01:00
//alert('set_readonly("'+_path+'", '+_ro+')');
if ( ! this . path _widget ) // widget not yet ready, try later
{
this . readonly = [ _path , _ro ] ;
return ;
}
2021-06-10 11:38:54 +02:00
for ( let id in this . path _widget ) {
let path = this . get _path ( id ) ;
2020-02-28 14:42:45 +01:00
if ( _path == path ) {
2021-06-10 11:38:54 +02:00
let ids = [ 'button[linkpaste]' , 'button[paste]' , 'button[createdir]' , 'button[symlink]' , 'upload' , 'new' ] ;
for ( let i = 0 ; i < ids . length ; ++ i ) {
let widget = etemplate2 . getById ( id ) . widgetContainer . getWidgetById ( ids [ i ] ) ;
2020-02-28 14:42:45 +01:00
if ( widget ) {
widget . set _readonly ( _ro ) ;
}
}
}
}
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Row or filename in select - file dialog clicked
*
* @ param { jQuery . event } event
* @ param { et2 _widget } widget
* /
2021-06-10 11:38:54 +02:00
select _clicked ( event , widget ) {
2021-03-16 14:09:25 +01:00
var _a ;
if ( ( _a = widget === null || widget === void 0 ? void 0 : widget . value ) === null || _a === void 0 ? void 0 : _a . is _dir ) {
2021-06-10 11:38:54 +02:00
let path = null ;
2020-02-28 14:42:45 +01:00
// Cannot do this, there are multiple widgets named path
// widget.getRoot().getWidgetById("path");
widget . getRoot ( ) . iterateOver ( function ( widget ) {
if ( widget . id == "path" )
2021-06-10 11:38:54 +02:00
path = widget ;
2020-02-28 14:42:45 +01:00
} , null , et2 _textbox ) ;
2021-06-10 11:38:54 +02:00
if ( path ) {
path . set _value ( widget . value . path ) ;
2020-02-28 14:42:45 +01:00
}
}
else if ( this . et2 && this . et2 . getArrayMgr ( 'content' ) . getEntry ( 'mode' ) != 'open-multiple' ) {
2021-06-10 11:38:54 +02:00
let editfield = this . et2 . getWidgetById ( 'name' ) ;
2020-02-28 14:42:45 +01:00
if ( editfield ) {
editfield . set _value ( widget . value . name ) ;
}
}
else {
2021-06-10 11:38:54 +02:00
let file = widget . value . name ;
2020-02-28 14:42:45 +01:00
widget . getParent ( ) . iterateOver ( function ( widget ) {
2021-06-10 11:38:54 +02:00
if ( widget . options . selected _value == file ) {
widget . set _value ( widget . get _value ( ) == file ? widget . options . unselected _value : file ) ;
2020-02-28 14:42:45 +01:00
}
} , null , et2 _checkbox ) ;
}
// Stop event or it will toggle back off
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
return false ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Set Sudo button ' s label and change its onclick handler according to its action
*
* @ param { widget object } _widget sudo buttononly
* @ param { string } _action string of action type { login | logout }
* /
2021-06-10 11:38:54 +02:00
set _sudoButton ( _widget , _action ) {
let widget = _widget || this . et2 . getWidgetById ( 'sudouser' ) ;
2020-02-28 14:42:45 +01:00
if ( widget ) {
switch ( _action ) {
case 'login' :
widget . set _label ( 'Logout' ) ;
2021-03-29 17:23:42 +02:00
widget . getRoot ( ) . getInstanceManager ( ) . submit ( widget ) ;
2020-02-28 14:42:45 +01:00
break ;
default :
widget . set _label ( 'Superuser' ) ;
widget . onclick = function ( ) {
jQuery ( '.superuser' ) . css ( 'display' , 'inline' ) ;
} ;
}
}
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Open file a file dialog from EPL , warn if EPL is not available
* /
2021-06-10 11:38:54 +02:00
fileafile ( ) {
2020-02-28 14:42:45 +01:00
if ( this . egw . user ( 'apps' ) . stylite ) {
this . egw . open _link ( '/index.php?menuaction=stylite.stylite_filemanager.upload&path=' + this . get _path ( ) , '_blank' , '670x320' ) ;
}
else {
2020-04-16 17:19:10 +02:00
// This is shown if stylite code is there, but the app is not available
2020-02-28 14:42:45 +01:00
et2 _dialog . show _dialog ( function ( _button ) {
if ( _button == et2 _dialog . YES _BUTTON )
window . open ( 'http://www.egroupware.org/EPL' , '_blank' ) ;
return true ;
2020-04-16 17:19:10 +02:00
} , this . egw . lang ( 'this feature is only available in epl version.' ) + "\n\n" +
2020-02-28 14:42:45 +01:00
this . egw . lang ( 'You can use regular upload [+] button to upload files.' ) + "\n\n" +
this . egw . lang ( 'Do you want more information about EPL subscription?' ) , this . egw . lang ( 'File a file' ) , undefined , et2 _dialog . BUTTONS _YES _NO , et2 _dialog . QUESTION _MESSAGE ) ;
}
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* create a share - link for the given entry
* Overriden from parent to handle empty directories
*
* @ param { egwAction } _action egw actions
* @ param { egwActionObject [ ] } _senders selected nm row
* @ param { egwActionObject } _target Drag source . Not used here .
* @ param { Boolean } _writable Allow edit access from the share .
* @ param { Boolean } _files Allow access to files from the share .
* @ param { Function } _callback Callback with results
* @ returns { Boolean } returns false if not successful
* /
2021-06-10 11:38:54 +02:00
share _link ( _action , _senders , _target , _writable , _files , _callback ) {
2020-02-28 14:42:45 +01:00
// Check to see if we're in the empty row (No matches found.) and use current path
2021-06-10 11:38:54 +02:00
let path = _senders [ 0 ] . id ;
2020-02-28 14:42:45 +01:00
if ( ! path ) {
_senders [ 0 ] = { id : this . get _path ( ) } ;
}
2020-03-25 18:39:22 +01:00
// Pass along any action data
2021-06-10 11:38:54 +02:00
let _extra = { } ;
for ( let i in _action . data ) {
2020-03-25 18:39:22 +01:00
if ( i . indexOf ( 'share' ) == 0 ) {
_extra [ i ] = _action . data [ i ] ;
}
}
2021-06-10 11:38:54 +02:00
super . share _link ( _action , _senders , _target , _writable , _files , _callback , _extra ) ;
}
2020-02-28 14:42:45 +01:00
/ * *
* Share - link callback
* @ param { object } _data
* /
2021-06-10 11:38:54 +02:00
_share _link _callback ( _data ) {
2020-02-28 14:42:45 +01:00
if ( _data . msg || _data . share _link )
2020-03-23 17:05:46 +01:00
window . egw _refresh ( _data . msg , this . appname ) ;
2020-02-28 14:42:45 +01:00
console . log ( "_data" , _data ) ;
2021-06-10 11:38:54 +02:00
let app = this ;
let copy _link _to _clipboard = function ( evt ) {
let $target = jQuery ( evt . target ) ;
2020-02-28 14:42:45 +01:00
$target . select ( ) ;
try {
2021-06-10 11:38:54 +02:00
let successful = document . execCommand ( 'copy' ) ;
2020-02-28 14:42:45 +01:00
if ( successful ) {
egw . message ( app . egw . lang ( 'Share link copied into clipboard' ) ) ;
return true ;
}
}
catch ( e ) { }
egw . message ( 'Failed to copy the link!' ) ;
} ;
jQuery ( "body" ) . on ( "click" , "[name=share_link]" , copy _link _to _clipboard ) ;
et2 _createWidget ( "dialog" , {
callback : function ( ) {
jQuery ( "body" ) . off ( "click" , "[name=share_link]" , copy _link _to _clipboard ) ;
return true ;
} ,
title : _data . title ? _data . title : ( _data . writable || _data . action === 'shareWritableLink' ?
this . egw . lang ( "Writable share link" ) : this . egw . lang ( "Readonly share link" ) ) ,
template : _data . template ,
width : 450 ,
value : { content : { "share_link" : _data . share _link } }
} ) ;
2021-06-10 11:38:54 +02:00
}
2020-03-25 03:34:04 +01:00
/ * *
* Check if a row can have the Hidden Uploads action
* Needs to be a directory
* /
2021-06-10 11:38:54 +02:00
hidden _upload _enabled ( _action , _senders ) {
2020-04-07 11:18:13 +02:00
if ( _senders [ 0 ] . id == 'nm' )
return false ;
2021-06-10 11:38:54 +02:00
let data = egw . dataGetUIDdata ( _senders [ 0 ] . id ) ;
let readonly = ( ( data === null || data === void 0 ? void 0 : data . data . class ) || '' ) . split ( / +/ ) . indexOf ( 'noEdit' ) >= 0 ;
2020-03-25 03:34:04 +01:00
// symlinks dont have mime 'http/unix-directory', but server marks all directories with class 'isDir'
2020-10-19 11:45:34 +02:00
return ( ! _senders [ 0 ] . id || data . data . is _dir && ! readonly ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* View the link from an existing share
* ( EPL only )
*
* @ param { egwAction } _action The shareLink action
* @ param { egwActionObject [ ] } _senders The row clicked on
* /
2021-06-10 11:38:54 +02:00
view _link ( _action , _senders ) {
let id = egw . dataGetUIDdata ( _senders [ 0 ] . id ) . data . share _id ;
2020-02-28 14:42:45 +01:00
egw . json ( 'stylite_filemanager::ajax_view_link' , [ id ] , this . _share _link _callback , this , true , this ) . sendRequest ( ) ;
return true ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* This function copies the selected file / folder entry as webdav link into clipboard
*
* @ param { object } _action egw actions
* @ param { object } _senders selected nm row
* @ returns { Boolean } returns false if not successful
* /
2021-06-10 11:38:54 +02:00
copy _link ( _action , _senders ) {
let data = egw . dataGetUIDdata ( _senders [ 0 ] . id ) ;
let url = data ? data . data . download _url : '/webdav.php' + this . id2path ( _senders [ 0 ] . id ) ;
2020-02-28 14:42:45 +01:00
if ( url [ 0 ] == '/' )
url = egw . link ( url ) ;
if ( url . substr ( 0 , 4 ) == 'http' && url . indexOf ( '://' ) <= 5 ) {
// it's already a full url
}
else {
2021-06-10 11:38:54 +02:00
let hostUrl = new URL ( window . location . href ) ;
2020-02-28 14:42:45 +01:00
url = hostUrl . origin + url ;
}
if ( url ) {
2021-06-10 11:38:54 +02:00
let elem = jQuery ( document . createElement ( 'div' ) ) ;
let range ;
2020-02-28 14:42:45 +01:00
elem . text ( url ) ;
elem . appendTo ( 'body' ) ;
if ( document . selection ) {
range = document . body . createTextRange ( ) ;
range . moveToElementText ( elem ) ;
range . select ( ) ;
}
else if ( window . getSelection ) {
range = document . createRange ( ) ;
range . selectNode ( elem [ 0 ] ) ;
window . getSelection ( ) . removeAllRanges ( ) ;
window . getSelection ( ) . addRange ( range ) ;
}
2021-06-10 11:38:54 +02:00
let successful = false ;
2020-02-28 14:42:45 +01:00
try {
successful = document . execCommand ( 'copy' ) ;
if ( successful ) {
egw . message ( this . egw . lang ( 'WebDav link copied into clipboard' ) ) ;
window . getSelection ( ) . removeAllRanges ( ) ;
return true ;
}
}
catch ( e ) { }
egw . message ( 'Failed to copy the link!' ) ;
elem . remove ( ) ;
return false ;
}
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Function to check wheter selected file is editable . ATM only . odt is supported .
*
* @ param { object } _egwAction egw action object
* @ param { object } _senders object of selected row
*
* @ returns { boolean } returns true if is editable otherwise false
* /
2021-06-10 11:38:54 +02:00
isEditable ( _egwAction , _senders ) {
2020-02-28 14:42:45 +01:00
if ( _senders . length > 1 )
return false ;
2021-06-10 11:38:54 +02:00
let data = egw . dataGetUIDdata ( _senders [ 0 ] . id ) ;
let mime = this . et2 . getInstanceManager ( ) . widgetContainer . getWidgetById ( '$row' ) ;
let fe = egw _get _file _editor _prefered _mimes ( data . data . mime ) ;
2020-02-28 14:42:45 +01:00
if ( fe && fe . mime && ! fe . mime [ data . data . mime ] )
return false ;
return ! ! data . data . mime . match ( mime . mime _odf _regex ) ;
2021-06-10 11:38:54 +02:00
}
2020-02-28 14:42:45 +01:00
/ * *
* Method to create a new document
* @ param { object } _action either action or node
* @ param { object } _selected either widget or selected row
*
* @ return { boolean } returns true
2020-03-16 12:45:47 +01:00
* /
2021-06-10 11:38:54 +02:00
create _new ( _action , _selected ) {
let fe = egw . link _get _registry ( 'filemanager-editor' ) ;
2020-03-16 12:45:47 +01:00
if ( fe && fe [ "edit" ] ) {
egw . open _link ( egw . link ( '/index.php' , {
menuaction : fe [ "edit" ] . menuaction
} ) , '' , fe [ "popup_edit" ] ) ;
}
return true ;
2021-06-10 11:38:54 +02:00
}
}
2020-02-28 14:42:45 +01:00
app . classes . filemanager = filemanagerAPP ;
//# sourceMappingURL=app.js.map