2024-06-06 03:55:47 -04:00
/ * *
* SPDX - FileCopyrightText : 2016 - 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX - FileCopyrightText : 2012 - 2016 ownCloud , Inc .
* SPDX - License - Identifier : AGPL - 3.0 - or - later
2014-10-31 06:41:07 -04:00
* /
2015-03-16 09:07:53 -04:00
2013-10-10 05:10:32 -04:00
( function ( ) {
2014-10-31 06:41:07 -04:00
/ * *
* Returns the selection of applicable users in the given configuration row
*
* @ param $row configuration row
* @ return array array of user names
* /
2014-08-21 09:11:21 -04:00
function getSelection ( $row ) {
var values = $row . find ( '.applicableUsers' ) . select2 ( 'val' ) ;
if ( ! values || values . length === 0 ) {
2014-10-31 06:41:07 -04:00
values = [ ] ;
2014-08-21 09:11:21 -04:00
}
return values ;
}
2023-01-11 06:30:59 -05:00
function getSelectedApplicable ( $row ) {
var users = [ ] ;
var groups = [ ] ;
var multiselect = getSelection ( $row ) ;
$ . each ( multiselect , function ( index , value ) {
// FIXME: don't rely on string parts to detect groups...
var pos = ( value . indexOf ) ? value . indexOf ( '(group)' ) : - 1 ;
if ( pos !== - 1 ) {
groups . push ( value . substr ( 0 , pos ) ) ;
} else {
users . push ( value ) ;
}
} ) ;
// FIXME: this should be done in the multiselect change event instead
$row . find ( '.applicable' )
. data ( 'applicable-groups' , groups )
. data ( 'applicable-users' , users ) ;
return { users , groups } ;
}
2014-10-13 12:40:57 -04:00
function highlightBorder ( $element , highlight ) {
$element . toggleClass ( 'warning-input' , highlight ) ;
2014-08-14 12:48:34 -04:00
return highlight ;
}
2016-01-26 12:25:59 -05:00
function isInputValid ( $input ) {
var optional = $input . hasClass ( 'optional' ) ;
switch ( $input . attr ( 'type' ) ) {
case 'text' :
case 'password' :
if ( $input . val ( ) === '' && ! optional ) {
return false ;
}
break ;
}
return true ;
}
2014-10-13 12:40:57 -04:00
function highlightInput ( $input ) {
2016-01-26 12:25:59 -05:00
switch ( $input . attr ( 'type' ) ) {
case 'text' :
case 'password' :
return highlightBorder ( $input , ! isInputValid ( $input ) ) ;
2014-08-14 12:48:34 -04:00
}
}
2014-10-31 06:41:07 -04:00
/ * *
* Initialize select2 plugin on the given elements
*
* @ param { Array < Object > } array of jQuery elements
2022-01-10 08:06:28 -05:00
* @ param { number } userListLimit page size for result list
2014-10-31 06:41:07 -04:00
* /
2023-01-11 06:30:59 -05:00
function initApplicableUsersMultiselect ( $elements , userListLimit ) {
2020-10-07 16:58:00 -04:00
var escapeHTML = function ( text ) {
return text . toString ( )
. split ( '&' ) . join ( '&' )
. split ( '<' ) . join ( '<' )
. split ( '>' ) . join ( '>' )
. split ( '"' ) . join ( '"' )
. split ( '\'' ) . join ( ''' ) ;
} ;
2014-10-31 06:41:07 -04:00
if ( ! $elements . length ) {
return ;
}
2023-01-11 06:30:59 -05:00
return $elements . select2 ( {
2022-09-21 11:44:32 -04:00
placeholder : t ( 'files_external' , 'Type to select account or group.' ) ,
2014-10-31 06:41:07 -04:00
allowClear : true ,
multiple : true ,
2017-07-08 09:56:19 -04:00
toggleSelect : true ,
2016-07-25 11:08:33 -04:00
dropdownCssClass : 'files-external-select2' ,
2014-10-31 06:41:07 -04:00
//minimumInputLength: 1,
ajax : {
url : OC . generateUrl ( 'apps/files_external/applicable' ) ,
dataType : 'json' ,
quietMillis : 100 ,
data : function ( term , page ) { // page is the one-based page number tracked by Select2
return {
pattern : term , //search term
limit : userListLimit , // page size
offset : userListLimit * ( page - 1 ) // page number starts with 0
} ;
} ,
results : function ( data ) {
if ( data . status === 'success' ) {
var results = [ ] ;
var userCount = 0 ; // users is an object
// add groups
2020-11-30 08:39:13 -05:00
$ . each ( data . groups , function ( gid , group ) {
results . push ( { name : gid + '(group)' , displayname : group , type : 'group' } ) ;
2014-10-31 06:41:07 -04:00
} ) ;
// add users
$ . each ( data . users , function ( id , user ) {
userCount ++ ;
results . push ( { name : id , displayname : user , type : 'user' } ) ;
} ) ;
var more = ( userCount >= userListLimit ) || ( data . groups . length >= userListLimit ) ;
return { results : results , more : more } ;
2012-06-08 11:42:00 -04:00
} else {
2014-10-31 06:41:07 -04:00
//FIXME add error handling
2012-06-08 11:42:00 -04:00
}
}
2014-10-31 06:41:07 -04:00
} ,
initSelection : function ( element , callback ) {
var users = { } ;
users [ 'users' ] = [ ] ;
var toSplit = element . val ( ) . split ( "," ) ;
for ( var i = 0 ; i < toSplit . length ; i ++ ) {
users [ 'users' ] . push ( toSplit [ i ] ) ;
}
$ . ajax ( OC . generateUrl ( 'displaynames' ) , {
type : 'POST' ,
contentType : 'application/json' ,
data : JSON . stringify ( users ) ,
dataType : 'json'
} ) . done ( function ( data ) {
var results = [ ] ;
if ( data . status === 'success' ) {
$ . each ( data . users , function ( user , displayname ) {
if ( displayname !== false ) {
results . push ( { name : user , displayname : displayname , type : 'user' } ) ;
2013-10-10 05:10:32 -04:00
}
2013-01-01 16:19:40 -05:00
} ) ;
2014-10-31 06:41:07 -04:00
callback ( results ) ;
} else {
//FIXME add error handling
}
} ) ;
} ,
id : function ( element ) {
return element . name ;
} ,
formatResult : function ( element ) {
2022-04-04 06:53:58 -04:00
var $result = $ ( '<span><div class="avatardiv"></div><span>' + escapeHTML ( element . displayname ) + '</span></span>' ) ;
2014-10-31 06:41:07 -04:00
var $div = $result . find ( '.avatardiv' )
. attr ( 'data-type' , element . type )
. attr ( 'data-name' , element . name )
. attr ( 'data-displayname' , element . displayname ) ;
if ( element . type === 'group' ) {
2018-07-24 12:48:25 -04:00
var url = OC . imagePath ( 'core' , 'actions/group' ) ;
2014-10-31 06:41:07 -04:00
$div . html ( '<img width="32" height="32" src="' + url + '">' ) ;
}
return $result . get ( 0 ) . outerHTML ;
} ,
formatSelection : function ( element ) {
if ( element . type === 'group' ) {
2021-04-20 05:33:31 -04:00
return '<span title="' + escapeHTML ( element . name ) + '" class="group">' + escapeHTML ( element . displayname + ' ' + t ( 'files_external' , '(Group)' ) ) + '</span>' ;
2012-06-08 11:42:00 -04:00
} else {
2014-10-31 06:41:07 -04:00
return '<span title="' + escapeHTML ( element . name ) + '" class="user">' + escapeHTML ( element . displayname ) + '</span>' ;
2012-06-08 11:42:00 -04:00
}
2014-10-31 06:41:07 -04:00
} ,
escapeMarkup : function ( m ) { return m ; } // we escape the markup in formatResult and formatSelection
} ) . on ( 'select2-loaded' , function ( ) {
$ . each ( $ ( '.avatardiv' ) , function ( i , div ) {
var $div = $ ( div ) ;
if ( $div . data ( 'type' ) === 'user' ) {
$div . avatar ( $div . data ( 'name' ) , 32 ) ;
}
} ) ;
2023-01-11 06:30:59 -05:00
} ) . on ( 'change' , function ( event ) {
highlightBorder ( $ ( event . target ) . closest ( '.applicableUsersContainer' ) . find ( '.select2-choices' ) , ! event . val . length ) ;
2014-10-31 06:41:07 -04:00
} ) ;
}
/ * *
2018-10-19 11:35:13 -04:00
* @ class OCA . Files _External . Settings . StorageConfig
2014-10-31 06:41:07 -04:00
*
* @ classdesc External storage config
* /
var StorageConfig = function ( id ) {
this . id = id ;
this . backendOptions = { } ;
} ;
2020-07-09 17:39:58 -04:00
// Keep this in sync with \OCA\Files_External\MountConfig::STATUS_*
2014-10-31 06:41:07 -04:00
StorageConfig . Status = {
IN _PROGRESS : - 1 ,
SUCCESS : 0 ,
2015-08-12 14:51:09 -04:00
ERROR : 1 ,
INDETERMINATE : 2
2012-09-05 16:17:33 -04:00
} ;
2016-01-26 07:22:27 -05:00
StorageConfig . Visibility = {
NONE : 0 ,
PERSONAL : 1 ,
ADMIN : 2 ,
DEFAULT : 3
} ;
2014-10-31 06:41:07 -04:00
/ * *
2018-10-19 11:35:13 -04:00
* @ memberof OCA . Files _External . Settings
2014-10-31 06:41:07 -04:00
* /
StorageConfig . prototype = {
_url : null ,
2012-06-12 11:36:25 -04:00
2014-10-31 06:41:07 -04:00
/ * *
* Storage id
*
* @ type int
* /
id : null ,
/ * *
* Mount point
*
* @ type string
* /
mountPoint : '' ,
/ * *
2015-08-12 15:03:11 -04:00
* Backend
2014-10-31 06:41:07 -04:00
*
* @ type string
* /
2015-08-12 15:03:11 -04:00
backend : null ,
2014-10-31 06:41:07 -04:00
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
/ * *
2015-08-12 15:03:11 -04:00
* Authentication mechanism
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
*
* @ type string
* /
2015-08-12 15:03:11 -04:00
authMechanism : null ,
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
2014-10-31 06:41:07 -04:00
/ * *
* Backend - specific configuration
*
* @ type Object . < string , object >
* /
backendOptions : null ,
2015-03-13 07:49:11 -04:00
/ * *
* Mount - specific options
*
* @ type Object . < string , object >
* /
mountOptions : null ,
2014-10-31 06:41:07 -04:00
/ * *
* Creates or saves the storage .
*
* @ param { Function } [ options . success ] success callback , receives result as argument
* @ param { Function } [ options . error ] error callback
* /
save : function ( options ) {
var url = OC . generateUrl ( this . _url ) ;
var method = 'POST' ;
if ( _ . isNumber ( this . id ) ) {
method = 'PUT' ;
url = OC . generateUrl ( this . _url + '/{id}' , { id : this . id } ) ;
}
2024-10-14 09:12:16 -04:00
window . OC . PasswordConfirmation . requirePasswordConfirmation ( ( ) => this . _save ( method , url , options ) , options . error ) ;
} ,
/ * *
* Private implementation of the save function ( called after potential password confirmation )
* @ param { string } method
* @ param { string } url
* @ param { { success : Function , error : Function } } options
* /
_save : function ( method , url , options ) {
self = this ;
2014-10-31 06:41:07 -04:00
$ . ajax ( {
type : method ,
url : url ,
2015-03-16 09:07:53 -04:00
contentType : 'application/json' ,
data : JSON . stringify ( this . getData ( ) ) ,
2014-10-31 06:41:07 -04:00
success : function ( result ) {
self . id = result . id ;
if ( _ . isFunction ( options . success ) ) {
options . success ( result ) ;
}
} ,
error : options . error
} ) ;
} ,
/ * *
* Returns the data from this object
*
* @ return { Array } JSON array of the data
* /
getData : function ( ) {
var data = {
mountPoint : this . mountPoint ,
2015-08-12 15:03:11 -04:00
backend : this . backend ,
authMechanism : this . authMechanism ,
2016-06-07 10:53:16 -04:00
backendOptions : this . backendOptions ,
2016-06-07 12:25:17 -04:00
testOnly : true
2014-10-31 06:41:07 -04:00
} ;
if ( this . id ) {
data . id = this . id ;
}
2015-03-13 07:49:11 -04:00
if ( this . mountOptions ) {
data . mountOptions = this . mountOptions ;
}
2014-10-31 06:41:07 -04:00
return data ;
} ,
/ * *
* Recheck the storage
*
* @ param { Function } [ options . success ] success callback , receives result as argument
* @ param { Function } [ options . error ] error callback
* /
recheck : function ( options ) {
if ( ! _ . isNumber ( this . id ) ) {
if ( _ . isFunction ( options . error ) ) {
options . error ( ) ;
2014-05-08 09:25:46 -04:00
}
2014-10-31 06:41:07 -04:00
return ;
2014-05-08 09:25:46 -04:00
}
2014-10-31 06:41:07 -04:00
$ . ajax ( {
type : 'GET' ,
url : OC . generateUrl ( this . _url + '/{id}' , { id : this . id } ) ,
2016-06-07 12:25:17 -04:00
data : { 'testOnly' : true } ,
2014-10-31 06:41:07 -04:00
success : options . success ,
error : options . error
} ) ;
} ,
2014-05-08 09:25:46 -04:00
2014-10-31 06:41:07 -04:00
/ * *
* Deletes the storage
*
* @ param { Function } [ options . success ] success callback
* @ param { Function } [ options . error ] error callback
* /
destroy : function ( options ) {
if ( ! _ . isNumber ( this . id ) ) {
// the storage hasn't even been created => success
if ( _ . isFunction ( options . success ) ) {
options . success ( ) ;
}
return ;
}
2024-10-14 09:12:16 -04:00
window . OC . PasswordConfirmation . requirePasswordConfirmation ( ( ) => this . _destroy ( options ) , options . error )
} ,
/ * *
* Private implementation of the DELETE method called after password confirmation
* @ param { { success : Function , error : Function } } options
* /
_destroy : function ( options ) {
2014-10-31 06:41:07 -04:00
$ . ajax ( {
type : 'DELETE' ,
url : OC . generateUrl ( this . _url + '/{id}' , { id : this . id } ) ,
success : options . success ,
error : options . error
} ) ;
} ,
2014-08-22 12:16:55 -04:00
2014-10-31 06:41:07 -04:00
/ * *
* Validate this model
*
* @ return { boolean } false if errors exist , true otherwise
* /
validate : function ( ) {
if ( this . mountPoint === '' ) {
return false ;
}
2016-01-19 08:16:11 -05:00
if ( ! this . backend ) {
return false ;
}
2014-10-31 06:41:07 -04:00
if ( this . errors ) {
return false ;
2014-05-08 09:25:46 -04:00
}
2014-10-31 06:41:07 -04:00
return true ;
2014-05-08 09:25:46 -04:00
}
2014-10-31 06:41:07 -04:00
} ;
/ * *
2018-10-19 11:35:13 -04:00
* @ class OCA . Files _External . Settings . GlobalStorageConfig
* @ augments OCA . Files _External . Settings . StorageConfig
2014-10-31 06:41:07 -04:00
*
* @ classdesc Global external storage config
* /
var GlobalStorageConfig = function ( id ) {
this . id = id ;
this . applicableUsers = [ ] ;
this . applicableGroups = [ ] ;
} ;
/ * *
2018-10-19 11:35:13 -04:00
* @ memberOf OCA . Files _External . Settings
2014-10-31 06:41:07 -04:00
* /
GlobalStorageConfig . prototype = _ . extend ( { } , StorageConfig . prototype ,
2018-10-19 11:35:13 -04:00
/** @lends OCA.Files_External.Settings.GlobalStorageConfig.prototype */ {
2014-10-31 06:41:07 -04:00
_url : 'apps/files_external/globalstorages' ,
/ * *
* Applicable users
*
* @ type Array . < string >
* /
applicableUsers : null ,
/ * *
* Applicable groups
*
* @ type Array . < string >
* /
applicableGroups : null ,
2015-03-12 12:27:31 -04:00
/ * *
* Storage priority
*
* @ type int
* /
priority : null ,
2014-10-31 06:41:07 -04:00
/ * *
* Returns the data from this object
*
* @ return { Array } JSON array of the data
* /
getData : function ( ) {
var data = StorageConfig . prototype . getData . apply ( this , arguments ) ;
return _ . extend ( data , {
applicableUsers : this . applicableUsers ,
applicableGroups : this . applicableGroups ,
2015-03-12 12:27:31 -04:00
priority : this . priority ,
2014-10-31 06:41:07 -04:00
} ) ;
}
} ) ;
/ * *
2018-10-19 11:35:13 -04:00
* @ class OCA . Files _External . Settings . UserStorageConfig
* @ augments OCA . Files _External . Settings . StorageConfig
2014-10-31 06:41:07 -04:00
*
* @ classdesc User external storage config
* /
var UserStorageConfig = function ( id ) {
this . id = id ;
} ;
UserStorageConfig . prototype = _ . extend ( { } , StorageConfig . prototype ,
2018-10-19 11:35:13 -04:00
/** @lends OCA.Files_External.Settings.UserStorageConfig.prototype */ {
2014-10-31 06:41:07 -04:00
_url : 'apps/files_external/userstorages'
} ) ;
2016-01-19 08:16:11 -05:00
/ * *
2018-10-19 11:35:13 -04:00
* @ class OCA . Files _External . Settings . UserGlobalStorageConfig
* @ augments OCA . Files _External . Settings . StorageConfig
2016-01-19 08:16:11 -05:00
*
* @ classdesc User external storage config
* /
var UserGlobalStorageConfig = function ( id ) {
this . id = id ;
} ;
UserGlobalStorageConfig . prototype = _ . extend ( { } , StorageConfig . prototype ,
2018-10-19 11:35:13 -04:00
/** @lends OCA.Files_External.Settings.UserStorageConfig.prototype */ {
2016-01-19 08:16:11 -05:00
2016-01-19 10:57:20 -05:00
_url : 'apps/files_external/userglobalstorages'
2016-01-19 08:16:11 -05:00
} ) ;
2015-03-16 09:07:53 -04:00
/ * *
2018-10-19 11:35:13 -04:00
* @ class OCA . Files _External . Settings . MountOptionsDropdown
2015-03-16 09:07:53 -04:00
*
* @ classdesc Dropdown for mount options
*
* @ param { Object } $container container DOM object
* /
var MountOptionsDropdown = function ( ) {
} ;
/ * *
2018-10-19 11:35:13 -04:00
* @ memberof OCA . Files _External . Settings
2015-03-16 09:07:53 -04:00
* /
MountOptionsDropdown . prototype = {
/ * *
* Dropdown element
*
* @ var Object
* /
$el : null ,
/ * *
* Show dropdown
*
* @ param { Object } $container container
* @ param { Object } mountOptions mount options
2016-05-02 11:34:24 -04:00
* @ param { Array } visibleOptions enabled mount options
2015-03-16 09:07:53 -04:00
* /
2016-05-02 11:34:24 -04:00
show : function ( $container , mountOptions , visibleOptions ) {
2015-03-16 09:07:53 -04:00
if ( MountOptionsDropdown . _last ) {
MountOptionsDropdown . _last . hide ( ) ;
}
2018-10-19 11:35:13 -04:00
var $el = $ ( OCA . Files _External . Templates . mountOptionsDropDown ( {
2018-10-19 02:41:01 -04:00
mountOptionsEncodingLabel : t ( 'files_external' , 'Compatibility with Mac NFD encoding (slow)' ) ,
mountOptionsEncryptLabel : t ( 'files_external' , 'Enable encryption' ) ,
mountOptionsPreviewsLabel : t ( 'files_external' , 'Enable previews' ) ,
mountOptionsSharingLabel : t ( 'files_external' , 'Enable sharing' ) ,
mountOptionsFilesystemCheckLabel : t ( 'files_external' , 'Check for changes' ) ,
mountOptionsFilesystemCheckOnce : t ( 'files_external' , 'Never' ) ,
mountOptionsFilesystemCheckDA : t ( 'files_external' , 'Once every direct access' ) ,
mountOptionsReadOnlyLabel : t ( 'files_external' , 'Read only' ) ,
2022-04-07 10:22:12 -04:00
deleteLabel : t ( 'files_external' , 'Disconnect' )
2016-05-19 10:38:28 -04:00
} ) ) ;
2015-03-16 09:07:53 -04:00
this . $el = $el ;
2020-08-20 10:28:03 -04:00
var storage = $container [ 0 ] . parentNode . className ;
this . setOptions ( mountOptions , visibleOptions , storage ) ;
2015-03-16 09:07:53 -04:00
this . $el . appendTo ( $container ) ;
MountOptionsDropdown . _last = this ;
this . $el . trigger ( 'show' ) ;
} ,
hide : function ( ) {
if ( this . $el ) {
this . $el . trigger ( 'hide' ) ;
this . $el . remove ( ) ;
this . $el = null ;
MountOptionsDropdown . _last = null ;
}
} ,
/ * *
* Returns the mount options from the dropdown controls
*
* @ return { Object } options mount options
* /
getOptions : function ( ) {
var options = { } ;
this . $el . find ( 'input, select' ) . each ( function ( ) {
var $this = $ ( this ) ;
var key = $this . attr ( 'name' ) ;
var value = null ;
if ( $this . attr ( 'type' ) === 'checkbox' ) {
value = $this . prop ( 'checked' ) ;
} else {
value = $this . val ( ) ;
}
if ( $this . attr ( 'data-type' ) === 'int' ) {
value = parseInt ( value , 10 ) ;
}
options [ key ] = value ;
} ) ;
return options ;
} ,
/ * *
* Sets the mount options to the dropdown controls
*
* @ param { Object } options mount options
2016-05-02 11:34:24 -04:00
* @ param { Array } visibleOptions enabled mount options
2015-03-16 09:07:53 -04:00
* /
2020-08-20 10:28:03 -04:00
setOptions : function ( options , visibleOptions , storage ) {
if ( storage === 'owncloud' ) {
var ind = visibleOptions . indexOf ( 'encrypt' ) ;
if ( ind > 0 ) {
visibleOptions . splice ( ind , 1 ) ;
}
}
2015-03-16 09:07:53 -04:00
var $el = this . $el ;
_ . each ( options , function ( value , key ) {
var $optionEl = $el . find ( 'input, select' ) . filterAttr ( 'name' , key ) ;
if ( $optionEl . attr ( 'type' ) === 'checkbox' ) {
if ( _ . isString ( value ) ) {
value = ( value === 'true' ) ;
}
$optionEl . prop ( 'checked' , ! ! value ) ;
} else {
$optionEl . val ( value ) ;
}
} ) ;
2015-03-31 10:25:33 -04:00
$el . find ( '.optionRow' ) . each ( function ( i , row ) {
var $row = $ ( row ) ;
var optionId = $row . find ( 'input, select' ) . attr ( 'name' ) ;
2018-08-01 03:19:52 -04:00
if ( visibleOptions . indexOf ( optionId ) === - 1 && ! $row . hasClass ( 'persistent' ) ) {
2015-03-31 10:25:33 -04:00
$row . hide ( ) ;
2015-04-02 12:31:26 -04:00
} else {
$row . show ( ) ;
2015-03-31 10:25:33 -04:00
}
} ) ;
2015-03-16 09:07:53 -04:00
}
} ;
2014-10-31 06:41:07 -04:00
/ * *
2018-10-19 11:35:13 -04:00
* @ class OCA . Files _External . Settings . MountConfigListView
2014-10-31 06:41:07 -04:00
*
* @ classdesc Mount configuration list view
*
* @ param { Object } $el DOM object containing the list
* @ param { Object } [ options ]
2022-01-10 08:06:28 -05:00
* @ param { number } [ options . userListLimit ] page size in applicable users dropdown
2014-10-31 06:41:07 -04:00
* /
var MountConfigListView = function ( $el , options ) {
this . initialize ( $el , options ) ;
} ;
2016-02-01 11:44:58 -05:00
MountConfigListView . ParameterFlags = {
OPTIONAL : 1 ,
USER _PROVIDED : 2
} ;
MountConfigListView . ParameterTypes = {
TEXT : 0 ,
BOOLEAN : 1 ,
PASSWORD : 2 ,
HIDDEN : 3
} ;
2014-10-31 06:41:07 -04:00
/ * *
2018-10-19 11:35:13 -04:00
* @ memberOf OCA . Files _External . Settings
2014-10-31 06:41:07 -04:00
* /
2015-08-12 17:01:21 -04:00
MountConfigListView . prototype = _ . extend ( {
2014-10-31 06:41:07 -04:00
/ * *
* jQuery element containing the config list
*
* @ type Object
* /
$el : null ,
/ * *
* Storage config class
*
* @ type Class
* /
_storageConfigClass : null ,
/ * *
* Flag whether the list is about user storage configs ( true )
* or global storage configs ( false )
2015-03-15 15:47:22 -04:00
*
2014-10-31 06:41:07 -04:00
* @ type bool
* /
_isPersonal : false ,
/ * *
* Page size in applicable users dropdown
*
* @ type int
* /
_userListLimit : 30 ,
/ * *
* List of supported backends
*
* @ type Object . < string , Object >
* /
_allBackends : null ,
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
/ * *
* List of all supported authentication mechanisms
*
* @ type Object . < string , Object >
* /
_allAuthMechanisms : null ,
2015-03-31 10:25:33 -04:00
_encryptionEnabled : false ,
2014-10-31 06:41:07 -04:00
/ * *
* @ param { Object } $el DOM object containing the list
* @ param { Object } [ options ]
2022-01-10 08:06:28 -05:00
* @ param { number } [ options . userListLimit ] page size in applicable users dropdown
2014-10-31 06:41:07 -04:00
* /
initialize : function ( $el , options ) {
2015-03-20 05:48:14 -04:00
var self = this ;
2014-10-31 06:41:07 -04:00
this . $el = $el ;
this . _isPersonal = ( $el . data ( 'admin' ) !== true ) ;
if ( this . _isPersonal ) {
2018-10-19 11:35:13 -04:00
this . _storageConfigClass = OCA . Files _External . Settings . UserStorageConfig ;
2014-10-31 06:41:07 -04:00
} else {
2018-10-19 11:35:13 -04:00
this . _storageConfigClass = OCA . Files _External . Settings . GlobalStorageConfig ;
2014-10-31 06:41:07 -04:00
}
if ( options && ! _ . isUndefined ( options . userListLimit ) ) {
this . _userListLimit = options . userListLimit ;
}
2015-03-31 10:25:33 -04:00
this . _encryptionEnabled = options . encryptionEnabled ;
2021-12-06 05:18:59 -05:00
this . _canCreateLocal = options . canCreateLocal ;
2015-03-31 10:25:33 -04:00
2014-10-31 06:41:07 -04:00
// read the backend config that was carefully crammed
// into the data-configurations attribute of the select
this . _allBackends = this . $el . find ( '.selectBackend' ) . data ( 'configurations' ) ;
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
this . _allAuthMechanisms = this . $el . find ( '#addMountPoint .authentication' ) . data ( 'mechanisms' ) ;
2014-10-31 06:41:07 -04:00
2015-08-12 17:01:21 -04:00
this . _initEvents ( ) ;
} ,
2015-03-20 05:48:14 -04:00
2015-08-12 17:01:21 -04:00
/ * *
* Custom JS event handlers
* Trigger callback for all existing configurations
* /
whenSelectBackend : function ( callback ) {
2022-07-07 12:19:03 -04:00
this . $el . find ( 'tbody tr:not(#addMountPoint):not(.externalStorageLoading)' ) . each ( function ( i , tr ) {
2015-09-13 18:23:42 -04:00
var backend = $ ( tr ) . find ( '.backend' ) . data ( 'identifier' ) ;
2015-08-12 17:01:21 -04:00
callback ( $ ( tr ) , backend ) ;
} ) ;
this . on ( 'selectBackend' , callback ) ;
} ,
whenSelectAuthMechanism : function ( callback ) {
var self = this ;
2022-07-07 12:19:03 -04:00
this . $el . find ( 'tbody tr:not(#addMountPoint):not(.externalStorageLoading)' ) . each ( function ( i , tr ) {
2015-08-12 17:01:21 -04:00
var authMechanism = $ ( tr ) . find ( '.selectAuthMechanism' ) . val ( ) ;
callback ( $ ( tr ) , authMechanism , self . _allAuthMechanisms [ authMechanism ] [ 'scheme' ] ) ;
} ) ;
this . on ( 'selectAuthMechanism' , callback ) ;
2014-10-31 06:41:07 -04:00
} ,
/ * *
* Initialize DOM event handlers
* /
_initEvents : function ( ) {
var self = this ;
2015-03-15 15:47:22 -04:00
var onChangeHandler = _ . bind ( this . _onChange , this ) ;
//this.$el.on('input', 'td input', onChangeHandler);
this . $el . on ( 'keyup' , 'td input' , onChangeHandler ) ;
this . $el . on ( 'paste' , 'td input' , onChangeHandler ) ;
this . $el . on ( 'change' , 'td input:checkbox' , onChangeHandler ) ;
this . $el . on ( 'change' , '.applicable' , onChangeHandler ) ;
2014-10-31 06:41:07 -04:00
this . $el . on ( 'click' , '.status>span' , function ( ) {
self . recheckStorageConfig ( $ ( this ) . closest ( 'tr' ) ) ;
} ) ;
2018-08-01 03:19:52 -04:00
this . $el . on ( 'click' , 'td.mountOptionsToggle .icon-delete' , function ( ) {
2014-10-31 06:41:07 -04:00
self . deleteStorageConfig ( $ ( this ) . closest ( 'tr' ) ) ;
} ) ;
2018-02-27 05:35:11 -05:00
this . $el . on ( 'click' , 'td.save>.icon-checkmark' , function ( ) {
2017-03-22 09:32:26 -04:00
self . saveStorageConfig ( $ ( this ) . closest ( 'tr' ) ) ;
} ) ;
2018-08-01 03:19:52 -04:00
this . $el . on ( 'click' , 'td.mountOptionsToggle>.icon-more' , function ( ) {
2022-07-26 07:04:46 -04:00
$ ( this ) . attr ( 'aria-expanded' , 'true' ) ;
2015-03-16 09:07:53 -04:00
self . _showMountOptionsDropdown ( $ ( this ) . closest ( 'tr' ) ) ;
} ) ;
2014-10-31 06:41:07 -04:00
this . $el . on ( 'change' , '.selectBackend' , _ . bind ( this . _onSelectBackend , this ) ) ;
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
this . $el . on ( 'change' , '.selectAuthMechanism' , _ . bind ( this . _onSelectAuthMechanism , this ) ) ;
2023-01-11 06:30:59 -05:00
this . $el . on ( 'change' , '.applicableToAllUsers' , _ . bind ( this . _onChangeApplicableToAllUsers , this ) ) ;
2014-10-31 06:41:07 -04:00
} ,
2015-03-15 15:47:22 -04:00
_onChange : function ( event ) {
var self = this ;
var $target = $ ( event . target ) ;
2015-03-16 09:07:53 -04:00
if ( $target . closest ( '.dropdown' ) . length ) {
// ignore dropdown events
return ;
}
2015-03-15 15:47:22 -04:00
highlightInput ( $target ) ;
var $tr = $target . closest ( 'tr' ) ;
2015-09-16 11:19:13 -04:00
this . updateStatus ( $tr , null ) ;
2015-03-15 15:47:22 -04:00
} ,
2014-10-31 06:41:07 -04:00
_onSelectBackend : function ( event ) {
var $target = $ ( event . target ) ;
var $tr = $target . closest ( 'tr' ) ;
2015-08-12 17:01:21 -04:00
2015-09-13 18:23:42 -04:00
var storageConfig = new this . _storageConfigClass ( ) ;
storageConfig . mountPoint = $tr . find ( '.mountPoint input' ) . val ( ) ;
storageConfig . backend = $target . val ( ) ;
2015-09-15 09:38:28 -04:00
$tr . find ( '.mountPoint input' ) . val ( '' ) ;
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
2024-07-29 06:21:13 -04:00
$tr . find ( '.selectBackend' ) . prop ( 'selectedIndex' , 0 )
2015-11-22 12:25:32 -05:00
var onCompletion = jQuery . Deferred ( ) ;
$tr = this . newStorage ( storageConfig , onCompletion ) ;
2023-01-11 06:30:59 -05:00
$tr . find ( '.applicableToAllUsers' ) . prop ( 'checked' , false ) . trigger ( 'change' ) ;
2015-11-22 12:25:32 -05:00
onCompletion . resolve ( ) ;
2015-08-19 15:04:22 -04:00
2015-09-13 18:23:42 -04:00
$tr . find ( 'td.configuration' ) . children ( ) . not ( '[type=hidden]' ) . first ( ) . focus ( ) ;
2015-08-19 15:04:22 -04:00
this . saveStorageConfig ( $tr ) ;
2014-10-31 06:41:07 -04:00
} ,
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
_onSelectAuthMechanism : function ( event ) {
var $target = $ ( event . target ) ;
var $tr = $target . closest ( 'tr' ) ;
2015-08-12 15:03:11 -04:00
var authMechanism = $target . val ( ) ;
2015-11-22 12:25:32 -05:00
var onCompletion = jQuery . Deferred ( ) ;
this . configureAuthMechanism ( $tr , authMechanism , onCompletion ) ;
onCompletion . resolve ( ) ;
this . saveStorageConfig ( $tr ) ;
} ,
2023-01-11 06:30:59 -05:00
_onChangeApplicableToAllUsers : function ( event ) {
var $target = $ ( event . target ) ;
var $tr = $target . closest ( 'tr' ) ;
var checked = $target . is ( ':checked' ) ;
$tr . find ( '.applicableUsersContainer' ) . toggleClass ( 'hidden' , checked ) ;
if ( ! checked ) {
$tr . find ( '.applicableUsers' ) . select2 ( 'val' , '' , true ) ;
}
this . saveStorageConfig ( $tr ) ;
} ,
2015-11-22 12:25:32 -05:00
/ * *
* Configure the storage config with a new authentication mechanism
*
* @ param { jQuery } $tr config row
* @ param { string } authMechanism
* @ param { jQuery . Deferred } onCompletion
* /
configureAuthMechanism : function ( $tr , authMechanism , onCompletion ) {
2015-08-12 15:03:11 -04:00
var authMechanismConfiguration = this . _allAuthMechanisms [ authMechanism ] ;
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
var $td = $tr . find ( 'td.configuration' ) ;
$td . find ( '.auth-param' ) . remove ( ) ;
2015-08-12 15:03:11 -04:00
$ . each ( authMechanismConfiguration [ 'configuration' ] , _ . partial (
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
this . writeParameterInput , $td , _ , _ , [ 'auth-param' ]
2016-01-19 08:16:11 -05:00
) . bind ( this ) ) ;
2015-08-19 15:04:22 -04:00
2015-08-12 17:01:21 -04:00
this . trigger ( 'selectAuthMechanism' ,
2015-11-22 12:25:32 -05:00
$tr , authMechanism , authMechanismConfiguration [ 'scheme' ] , onCompletion
2015-08-12 17:01:21 -04:00
) ;
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
} ,
2015-09-13 18:23:42 -04:00
/ * *
* Create a config row for a new storage
*
* @ param { StorageConfig } storageConfig storage config to pull values from
2015-11-22 12:25:32 -05:00
* @ param { jQuery . Deferred } onCompletion
2022-02-28 10:52:55 -05:00
* @ param { boolean } deferAppend
2015-09-13 18:23:42 -04:00
* @ return { jQuery } created row
* /
2022-02-28 10:52:55 -05:00
newStorage : function ( storageConfig , onCompletion , deferAppend ) {
2015-09-13 18:23:42 -04:00
var mountPoint = storageConfig . mountPoint ;
var backend = this . _allBackends [ storageConfig . backend ] ;
2016-11-03 07:14:44 -04:00
if ( ! backend ) {
backend = {
name : 'Unknown: ' + storageConfig . backend ,
invalid : true
} ;
}
2015-09-13 18:23:42 -04:00
// FIXME: Replace with a proper Handlebar template
2022-02-28 10:52:55 -05:00
var $template = this . $el . find ( 'tr#addMountPoint' ) ;
var $tr = $template . clone ( ) ;
if ( ! deferAppend ) {
$tr . insertBefore ( $template ) ;
}
2015-09-13 18:23:42 -04:00
2016-01-19 08:16:11 -05:00
$tr . data ( 'storageConfig' , storageConfig ) ;
2016-03-16 18:06:49 -04:00
$tr . show ( ) ;
2017-03-22 09:32:26 -04:00
$tr . find ( 'td.mountOptionsToggle, td.save, td.remove' ) . removeClass ( 'hidden' ) ;
2015-09-13 18:23:42 -04:00
$tr . find ( 'td' ) . last ( ) . removeAttr ( 'style' ) ;
$tr . removeAttr ( 'id' ) ;
$tr . find ( 'select#selectBackend' ) ;
2022-02-28 10:52:55 -05:00
if ( ! deferAppend ) {
2023-01-11 06:30:59 -05:00
initApplicableUsersMultiselect ( $tr . find ( '.applicableUsers' ) , this . _userListLimit ) ;
2022-02-28 10:52:55 -05:00
}
2015-09-13 18:23:42 -04:00
if ( storageConfig . id ) {
$tr . data ( 'id' , storageConfig . id ) ;
}
$tr . find ( '.backend' ) . text ( backend . name ) ;
if ( mountPoint === '' ) {
mountPoint = this . _suggestMountPoint ( backend . name ) ;
}
$tr . find ( '.mountPoint input' ) . val ( mountPoint ) ;
$tr . addClass ( backend . identifier ) ;
$tr . find ( '.backend' ) . data ( 'identifier' , backend . identifier ) ;
2021-12-06 05:18:59 -05:00
if ( backend . invalid || ( backend . identifier === 'local' && ! this . _canCreateLocal ) ) {
2016-11-03 07:14:44 -04:00
$tr . find ( '[name=mountPoint]' ) . prop ( 'disabled' , true ) ;
$tr . find ( '.applicable,.mountOptionsToggle' ) . empty ( ) ;
2021-12-06 05:18:59 -05:00
$tr . find ( '.save' ) . empty ( ) ;
if ( backend . invalid ) {
2024-07-29 06:20:18 -04:00
this . updateStatus ( $tr , false , t ( 'files_external' , 'Unknown backend: {backendName}' , { backendName : backend . name } ) ) ;
2021-12-06 05:18:59 -05:00
}
2016-11-03 07:14:44 -04:00
return $tr ;
}
2015-09-13 18:23:42 -04:00
var selectAuthMechanism = $ ( '<select class="selectAuthMechanism"></select>' ) ;
2016-01-26 07:22:27 -05:00
var neededVisibility = ( this . _isPersonal ) ? StorageConfig . Visibility . PERSONAL : StorageConfig . Visibility . ADMIN ;
2015-09-13 18:23:42 -04:00
$ . each ( this . _allAuthMechanisms , function ( authIdentifier , authMechanism ) {
2016-01-19 08:21:59 -05:00
if ( backend . authSchemes [ authMechanism . scheme ] && ( authMechanism . visibility & neededVisibility ) ) {
2015-09-13 18:23:42 -04:00
selectAuthMechanism . append (
$ ( '<option value="' + authMechanism . identifier + '" data-scheme="' + authMechanism . scheme + '">' + authMechanism . name + '</option>' )
) ;
}
} ) ;
if ( storageConfig . authMechanism ) {
selectAuthMechanism . val ( storageConfig . authMechanism ) ;
2015-11-22 12:25:32 -05:00
} else {
storageConfig . authMechanism = selectAuthMechanism . val ( ) ;
2015-09-13 18:23:42 -04:00
}
$tr . find ( 'td.authentication' ) . append ( selectAuthMechanism ) ;
var $td = $tr . find ( 'td.configuration' ) ;
2016-01-19 08:16:11 -05:00
$ . each ( backend . configuration , _ . partial ( this . writeParameterInput , $td ) . bind ( this ) ) ;
2015-09-13 18:23:42 -04:00
2015-11-22 12:25:32 -05:00
this . trigger ( 'selectBackend' , $tr , backend . identifier , onCompletion ) ;
this . configureAuthMechanism ( $tr , storageConfig . authMechanism , onCompletion ) ;
2015-09-13 18:23:42 -04:00
if ( storageConfig . backendOptions ) {
2016-06-15 09:24:01 -04:00
$td . find ( 'input, select' ) . each ( function ( ) {
2015-09-13 18:23:42 -04:00
var input = $ ( this ) ;
2015-10-20 08:26:46 -04:00
var val = storageConfig . backendOptions [ input . data ( 'parameter' ) ] ;
if ( val !== undefined ) {
2016-02-24 13:49:03 -05:00
if ( input . is ( 'input:checkbox' ) ) {
input . prop ( 'checked' , val ) ;
}
2015-10-20 08:26:46 -04:00
input . val ( storageConfig . backendOptions [ input . data ( 'parameter' ) ] ) ;
highlightInput ( input ) ;
}
2015-09-13 18:23:42 -04:00
} ) ;
}
2015-09-14 15:21:16 -04:00
var applicable = [ ] ;
if ( storageConfig . applicableUsers ) {
applicable = applicable . concat ( storageConfig . applicableUsers ) ;
}
if ( storageConfig . applicableGroups ) {
applicable = applicable . concat (
_ . map ( storageConfig . applicableGroups , function ( group ) {
return group + '(group)' ;
} )
) ;
}
2023-01-11 06:30:59 -05:00
if ( applicable . length ) {
$tr . find ( '.applicableUsers' ) . val ( applicable ) . trigger ( 'change' )
$tr . find ( '.applicableUsersContainer' ) . removeClass ( 'hidden' ) ;
} else {
// applicable to all
$tr . find ( '.applicableUsersContainer' ) . addClass ( 'hidden' ) ;
}
$tr . find ( '.applicableToAllUsers' ) . prop ( 'checked' , ! applicable . length ) ;
2015-09-14 15:21:16 -04:00
2015-09-13 18:23:42 -04:00
var priorityEl = $ ( '<input type="hidden" class="priority" value="' + backend . priority + '" />' ) ;
$tr . append ( priorityEl ) ;
if ( storageConfig . mountOptions ) {
$tr . find ( 'input.mountOptions' ) . val ( JSON . stringify ( storageConfig . mountOptions ) ) ;
} else {
// FIXME default backend mount options
$tr . find ( 'input.mountOptions' ) . val ( JSON . stringify ( {
'encrypt' : true ,
'previews' : true ,
2016-02-23 09:16:13 -05:00
'enable_sharing' : false ,
2016-05-02 11:34:24 -04:00
'filesystem_check_changes' : 1 ,
2018-02-27 08:06:14 -05:00
'encoding_compatibility' : false ,
'readonly' : false ,
2015-09-13 18:23:42 -04:00
} ) ) ;
}
return $tr ;
} ,
/ * *
* Load storages into config rows
* /
loadStorages : function ( ) {
var self = this ;
2021-12-06 06:41:55 -05:00
var onLoaded1 = $ . Deferred ( ) ;
var onLoaded2 = $ . Deferred ( ) ;
this . $el . find ( '.externalStorageLoading' ) . removeClass ( 'hidden' ) ;
$ . when ( onLoaded1 , onLoaded2 ) . always ( ( ) => {
self . $el . find ( '.externalStorageLoading' ) . addClass ( 'hidden' ) ;
} )
2015-09-14 08:57:49 -04:00
if ( this . _isPersonal ) {
// load userglobal storages
$ . ajax ( {
type : 'GET' ,
url : OC . generateUrl ( 'apps/files_external/userglobalstorages' ) ,
2016-06-07 12:25:17 -04:00
data : { 'testOnly' : true } ,
2015-09-14 08:57:49 -04:00
contentType : 'application/json' ,
success : function ( result ) {
2024-07-29 06:08:55 -04:00
result = Object . values ( result ) ;
2015-11-22 12:25:32 -05:00
var onCompletion = jQuery . Deferred ( ) ;
2022-02-28 10:52:55 -05:00
var $rows = $ ( ) ;
2024-07-29 06:08:55 -04:00
result . forEach ( function ( storageParams ) {
2016-01-19 08:16:11 -05:00
var storageConfig ;
2016-01-19 10:57:20 -05:00
var isUserGlobal = storageParams . type === 'system' && self . _isPersonal ;
2015-09-14 08:57:49 -04:00
storageParams . mountPoint = storageParams . mountPoint . substr ( 1 ) ; // trim leading slash
2016-01-19 10:57:20 -05:00
if ( isUserGlobal ) {
2016-01-19 08:16:11 -05:00
storageConfig = new UserGlobalStorageConfig ( ) ;
} else {
storageConfig = new self . _storageConfigClass ( ) ;
}
2015-09-14 08:57:49 -04:00
_ . extend ( storageConfig , storageParams ) ;
2022-02-28 10:52:55 -05:00
var $tr = self . newStorage ( storageConfig , onCompletion , true ) ;
2015-09-14 08:57:49 -04:00
// userglobal storages must be at the top of the list
$tr . detach ( ) ;
self . $el . prepend ( $tr ) ;
var $authentication = $tr . find ( '.authentication' ) ;
$authentication . text ( $authentication . find ( 'select option:selected' ) . text ( ) ) ;
// disable any other inputs
$tr . find ( '.mountOptionsToggle, .remove' ) . empty ( ) ;
2016-01-19 08:16:11 -05:00
$tr . find ( 'input:not(.user_provided), select:not(.user_provided)' ) . attr ( 'disabled' , 'disabled' ) ;
2016-01-19 10:57:20 -05:00
if ( isUserGlobal ) {
2016-01-19 08:16:11 -05:00
$tr . find ( '.configuration' ) . find ( ':not(.user_provided)' ) . remove ( ) ;
} else {
// userglobal storages do not expose configuration data
$tr . find ( '.configuration' ) . text ( t ( 'files_external' , 'Admin defined' ) ) ;
}
2024-07-29 06:09:38 -04:00
// don't recheck config automatically when there are a large number of storages
if ( result . length < 20 ) {
self . recheckStorageConfig ( $tr ) ;
} else {
self . updateStatus ( $tr , StorageConfig . Status . INDETERMINATE , t ( 'files_external' , 'Automatic status checking is disabled due to the large number of configured storages, click to check status' ) ) ;
}
2022-02-28 10:52:55 -05:00
$rows = $rows . add ( $tr ) ;
2015-09-14 08:57:49 -04:00
} ) ;
2023-01-11 06:30:59 -05:00
initApplicableUsersMultiselect ( self . $el . find ( '.applicableUsers' ) , this . _userListLimit ) ;
2022-02-28 10:52:55 -05:00
self . $el . find ( 'tr#addMountPoint' ) . before ( $rows ) ;
2016-08-09 08:19:15 -04:00
var mainForm = $ ( '#files_external' ) ;
if ( result . length === 0 && mainForm . attr ( 'data-can-create' ) === 'false' ) {
mainForm . hide ( ) ;
$ ( 'a[href="#external-storage"]' ) . parent ( ) . hide ( ) ;
2022-07-22 10:11:01 -04:00
$ ( '.emptycontent' ) . show ( ) ;
2016-08-09 08:19:15 -04:00
}
2015-11-22 12:25:32 -05:00
onCompletion . resolve ( ) ;
2021-12-06 06:41:55 -05:00
onLoaded1 . resolve ( ) ;
2015-09-14 08:57:49 -04:00
}
} ) ;
2021-12-06 06:41:55 -05:00
} else {
onLoaded1 . resolve ( ) ;
2015-09-14 08:57:49 -04:00
}
2015-09-13 18:23:42 -04:00
var url = this . _storageConfigClass . prototype . _url ;
$ . ajax ( {
type : 'GET' ,
url : OC . generateUrl ( url ) ,
contentType : 'application/json' ,
success : function ( result ) {
2022-02-28 11:02:17 -05:00
result = Object . values ( result ) ;
2015-11-22 12:25:32 -05:00
var onCompletion = jQuery . Deferred ( ) ;
2022-02-28 10:52:55 -05:00
var $rows = $ ( ) ;
2022-02-28 11:02:17 -05:00
result . forEach ( function ( storageParams ) {
2017-02-10 07:29:18 -05:00
storageParams . mountPoint = ( storageParams . mountPoint === '/' ) ? '/' : storageParams . mountPoint . substr ( 1 ) ; // trim leading slash
2015-09-13 18:23:42 -04:00
var storageConfig = new self . _storageConfigClass ( ) ;
_ . extend ( storageConfig , storageParams ) ;
2022-02-28 10:52:55 -05:00
var $tr = self . newStorage ( storageConfig , onCompletion , true ) ;
2021-12-06 05:18:59 -05:00
2022-02-28 11:02:17 -05:00
// don't recheck config automatically when there are a large number of storages
if ( result . length < 20 ) {
self . recheckStorageConfig ( $tr ) ;
} else {
self . updateStatus ( $tr , StorageConfig . Status . INDETERMINATE , t ( 'files_external' , 'Automatic status checking is disabled due to the large number of configured storages, click to check status' ) ) ;
}
2022-02-28 10:52:55 -05:00
$rows = $rows . add ( $tr ) ;
2015-09-13 18:23:42 -04:00
} ) ;
2023-01-11 06:30:59 -05:00
initApplicableUsersMultiselect ( $rows . find ( '.applicableUsers' ) , this . _userListLimit ) ;
2022-02-28 10:52:55 -05:00
self . $el . find ( 'tr#addMountPoint' ) . before ( $rows ) ;
2015-11-22 12:25:32 -05:00
onCompletion . resolve ( ) ;
2021-12-06 06:41:55 -05:00
onLoaded2 . resolve ( ) ;
2015-09-13 18:23:42 -04:00
}
} ) ;
} ,
/ * *
* @ param { jQuery } $td
* @ param { string } parameter
* @ param { string } placeholder
* @ param { Array } classes
* @ return { jQuery } newly created input
* /
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
writeParameterInput : function ( $td , parameter , placeholder , classes ) {
2016-01-19 08:16:11 -05:00
var hasFlag = function ( flag ) {
2016-02-01 11:44:58 -05:00
return ( placeholder . flags & flag ) === flag ;
2016-01-19 08:16:11 -05:00
} ;
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
classes = $ . isArray ( classes ) ? classes : [ ] ;
classes . push ( 'added' ) ;
2016-02-01 11:44:58 -05:00
if ( hasFlag ( MountConfigListView . ParameterFlags . OPTIONAL ) ) {
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
classes . push ( 'optional' ) ;
}
2016-01-19 08:16:11 -05:00
2016-02-01 11:44:58 -05:00
if ( hasFlag ( MountConfigListView . ParameterFlags . USER _PROVIDED ) ) {
2016-01-19 08:16:11 -05:00
if ( this . _isPersonal ) {
classes . push ( 'user_provided' ) ;
} else {
return ;
}
}
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
var newElement ;
2016-01-19 08:16:11 -05:00
2016-02-01 11:44:58 -05:00
var trimmedPlaceholder = placeholder . value ;
if ( placeholder . type === MountConfigListView . ParameterTypes . PASSWORD ) {
2016-01-19 08:16:11 -05:00
newElement = $ ( '<input type="password" class="' + classes . join ( ' ' ) + '" data-parameter="' + parameter + '" placeholder="' + trimmedPlaceholder + '" />' ) ;
2016-02-01 11:44:58 -05:00
} else if ( placeholder . type === MountConfigListView . ParameterTypes . BOOLEAN ) {
2015-09-17 11:39:27 -04:00
var checkboxId = _ . uniqueId ( 'checkbox_' ) ;
2016-06-15 09:24:01 -04:00
newElement = $ ( '<div><label><input type="checkbox" id="' + checkboxId + '" class="' + classes . join ( ' ' ) + '" data-parameter="' + parameter + '" />' + trimmedPlaceholder + '</label></div>' ) ;
2016-02-01 11:44:58 -05:00
} else if ( placeholder . type === MountConfigListView . ParameterTypes . HIDDEN ) {
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
newElement = $ ( '<input type="hidden" class="' + classes . join ( ' ' ) + '" data-parameter="' + parameter + '" />' ) ;
} else {
2016-01-19 08:16:11 -05:00
newElement = $ ( '<input type="text" class="' + classes . join ( ' ' ) + '" data-parameter="' + parameter + '" placeholder="' + trimmedPlaceholder + '" />' ) ;
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
}
2020-04-02 11:22:42 -04:00
2023-01-11 08:39:39 -05:00
if ( placeholder . defaultValue ) {
if ( placeholder . type === MountConfigListView . ParameterTypes . BOOLEAN ) {
newElement . find ( 'input' ) . prop ( 'checked' , placeholder . defaultValue ) ;
} else {
newElement . val ( placeholder . defaultValue ) ;
}
}
2020-04-02 11:22:42 -04:00
if ( placeholder . tooltip ) {
newElement . attr ( 'title' , placeholder . tooltip ) ;
}
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
highlightInput ( newElement ) ;
$td . append ( newElement ) ;
2015-09-13 18:23:42 -04:00
return newElement ;
Authentication mechanisms for external storage backends
A backend can now specify generic authentication schemes that it
supports, instead of specifying the parameters for its authentication
method directly. This allows multiple authentication mechanisms to be
implemented for a single scheme, providing altered functionality.
This commit introduces the backend framework for this feature, and so at
this point the UI will be broken as the frontend does not specify the
required information.
Terminology:
- authentication scheme
Parameter interface for the authentication method. A backend
supporting the 'password' scheme accepts two parameters, 'user' and
'password'.
- authentication mechanism
Specific mechanism implementing a scheme. Basic mechanisms may
forward configuration options directly to the backend, more advanced
ones may lookup parameters or retrieve them from the session
New dropdown selector for external storage configurations to select the
authentication mechanism to be used.
Authentication mechanisms can have visibilities, just like backends.
The API was extended too to make it easier to add/remove visibilities.
In addition, the concept of 'allowed visibility' has been introduced, so
a backend/auth mechanism can force a maximum visibility level (e.g.
Local storage type) that cannot be overridden by configuration in the
web UI.
An authentication mechanism is a fully instantiated implementation. This
allows an implementation to have dependencies injected into it, e.g. an
\OCP\IDB for database operations.
When a StorageConfig is being prepared for mounting, the authentication
mechanism implementation has manipulateStorage() called,
which inserts the relevant authentication method options into the
storage ready for mounting.
2015-08-12 05:54:03 -04:00
} ,
2014-10-31 06:41:07 -04:00
/ * *
* Gets the storage model from the given row
*
* @ param $tr row element
2018-10-19 11:35:13 -04:00
* @ return { OCA . Files _External . StorageConfig } storage model instance
2014-10-31 06:41:07 -04:00
* /
getStorageConfig : function ( $tr ) {
2015-09-13 18:23:42 -04:00
var storageId = $tr . data ( 'id' ) ;
2014-10-31 06:41:07 -04:00
if ( ! storageId ) {
// new entry
storageId = null ;
}
2016-01-19 08:16:11 -05:00
var storage = $tr . data ( 'storageConfig' ) ;
if ( ! storage ) {
storage = new this . _storageConfigClass ( storageId ) ;
}
storage . errors = null ;
2014-10-31 06:41:07 -04:00
storage . mountPoint = $tr . find ( '.mountPoint input' ) . val ( ) ;
2015-09-13 18:23:42 -04:00
storage . backend = $tr . find ( '.backend' ) . data ( 'identifier' ) ;
2015-08-12 15:03:11 -04:00
storage . authMechanism = $tr . find ( '.selectAuthMechanism' ) . val ( ) ;
2014-10-31 06:41:07 -04:00
var classOptions = { } ;
var configuration = $tr . find ( '.configuration input' ) ;
var missingOptions = [ ] ;
$ . each ( configuration , function ( index , input ) {
var $input = $ ( input ) ;
var parameter = $input . data ( 'parameter' ) ;
if ( $input . attr ( 'type' ) === 'button' ) {
return ;
}
2016-01-19 08:16:11 -05:00
if ( ! isInputValid ( $input ) && ! $input . hasClass ( 'optional' ) ) {
2014-10-31 06:41:07 -04:00
missingOptions . push ( parameter ) ;
return ;
}
if ( $ ( input ) . is ( ':checkbox' ) ) {
if ( $ ( input ) . is ( ':checked' ) ) {
classOptions [ parameter ] = true ;
} else {
classOptions [ parameter ] = false ;
}
} else {
classOptions [ parameter ] = $ ( input ) . val ( ) ;
}
} ) ;
storage . backendOptions = classOptions ;
if ( missingOptions . length ) {
storage . errors = {
backendOptions : missingOptions
} ;
}
// gather selected users and groups
if ( ! this . _isPersonal ) {
2023-01-11 06:30:59 -05:00
var multiselect = getSelectedApplicable ( $tr ) ;
var users = multiselect . users || [ ] ;
var groups = multiselect . groups || [ ] ;
var isApplicableToAllUsers = $tr . find ( '.applicableToAllUsers' ) . is ( ':checked' ) ;
if ( isApplicableToAllUsers ) {
storage . applicableUsers = [ ] ;
storage . applicableGroups = [ ] ;
} else {
storage . applicableUsers = users ;
storage . applicableGroups = groups ;
2014-10-31 06:41:07 -04:00
2023-01-11 06:30:59 -05:00
if ( ! storage . applicableUsers . length && ! storage . applicableGroups . length ) {
if ( ! storage . errors ) {
storage . errors = { } ;
}
storage . errors [ 'requiredApplicable' ] = true ;
}
}
2015-03-12 12:27:31 -04:00
2015-03-16 09:07:53 -04:00
storage . priority = parseInt ( $tr . find ( 'input.priority' ) . val ( ) || '100' , 10 ) ;
2014-10-31 06:41:07 -04:00
}
2015-03-13 07:49:11 -04:00
var mountOptions = $tr . find ( 'input.mountOptions' ) . val ( ) ;
if ( mountOptions ) {
storage . mountOptions = JSON . parse ( mountOptions ) ;
}
2014-10-31 06:41:07 -04:00
return storage ;
} ,
2012-06-12 11:36:25 -04:00
2014-10-31 06:41:07 -04:00
/ * *
* Deletes the storage from the given tr
*
* @ param $tr storage row
* @ param Function callback callback to call after save
* /
deleteStorageConfig : function ( $tr ) {
var self = this ;
var configId = $tr . data ( 'id' ) ;
if ( ! _ . isNumber ( configId ) ) {
// deleting unsaved storage
$tr . remove ( ) ;
return ;
}
var storage = new this . _storageConfigClass ( configId ) ;
2022-04-07 10:22:12 -04:00
OC . dialogs . confirm ( t ( 'files_external' , 'Are you sure you want to disconnect this external storage? It will make the storage unavailable in Nextcloud and will lead to a deletion of these files and folders on any sync client that is currently connected but will not delete any files and folders on the external storage itself.' , {
2017-09-27 11:59:56 -04:00
storage : this . mountPoint
} ) , t ( 'files_external' , 'Delete storage?' ) , function ( confirm ) {
if ( confirm ) {
2017-10-11 10:25:36 -04:00
self . updateStatus ( $tr , StorageConfig . Status . IN _PROGRESS ) ;
2017-09-27 11:59:56 -04:00
storage . destroy ( {
success : function ( ) {
$tr . remove ( ) ;
} ,
2024-07-29 06:15:50 -04:00
error : function ( result ) {
const statusMessage = ( result && result . responseJSON ) ? result . responseJSON . message : undefined ;
self . updateStatus ( $tr , StorageConfig . Status . ERROR , statusMessage ) ;
2017-09-27 11:59:56 -04:00
}
} ) ;
2014-10-31 06:41:07 -04:00
}
} ) ;
} ,
/ * *
* Saves the storage from the given tr
*
* @ param $tr storage row
* @ param Function callback callback to call after save
2015-09-16 11:19:13 -04:00
* @ param concurrentTimer only update if the timer matches this
2014-10-31 06:41:07 -04:00
* /
2015-09-16 11:19:13 -04:00
saveStorageConfig : function ( $tr , callback , concurrentTimer ) {
2014-10-31 06:41:07 -04:00
var self = this ;
var storage = this . getStorageConfig ( $tr ) ;
2016-01-19 08:16:11 -05:00
if ( ! storage || ! storage . validate ( ) ) {
2014-10-31 06:41:07 -04:00
return false ;
}
this . updateStatus ( $tr , StorageConfig . Status . IN _PROGRESS ) ;
storage . save ( {
success : function ( result ) {
2015-09-16 11:19:13 -04:00
if ( concurrentTimer === undefined
|| $tr . data ( 'save-timer' ) === concurrentTimer
) {
2024-07-29 06:15:18 -04:00
self . updateStatus ( $tr , result . status , result . statusMessage ) ;
2015-09-13 18:23:42 -04:00
$tr . data ( 'id' , result . id ) ;
2015-09-16 11:19:13 -04:00
if ( _ . isFunction ( callback ) ) {
callback ( storage ) ;
}
2014-10-31 06:41:07 -04:00
}
} ,
2024-07-29 06:15:50 -04:00
error : function ( result ) {
2015-09-16 11:19:13 -04:00
if ( concurrentTimer === undefined
|| $tr . data ( 'save-timer' ) === concurrentTimer
) {
2024-07-29 06:15:50 -04:00
const statusMessage = ( result && result . responseJSON ) ? result . responseJSON . message : undefined ;
self . updateStatus ( $tr , StorageConfig . Status . ERROR , statusMessage ) ;
2015-09-16 11:19:13 -04:00
}
2014-10-31 06:41:07 -04:00
}
} ) ;
} ,
/ * *
* Recheck storage availability
*
* @ param { jQuery } $tr storage row
* @ return { boolean } success
* /
recheckStorageConfig : function ( $tr ) {
var self = this ;
var storage = this . getStorageConfig ( $tr ) ;
if ( ! storage . validate ( ) ) {
return false ;
}
this . updateStatus ( $tr , StorageConfig . Status . IN _PROGRESS ) ;
storage . recheck ( {
success : function ( result ) {
2015-09-16 11:58:26 -04:00
self . updateStatus ( $tr , result . status , result . statusMessage ) ;
2014-10-31 06:41:07 -04:00
} ,
2024-07-29 06:15:50 -04:00
error : function ( result ) {
const statusMessage = ( result && result . responseJSON ) ? result . responseJSON . message : undefined ;
self . updateStatus ( $tr , StorageConfig . Status . ERROR , statusMessage ) ;
2014-10-31 06:41:07 -04:00
}
} ) ;
} ,
/ * *
* Update status display
*
* @ param { jQuery } $tr
2022-01-10 08:06:28 -05:00
* @ param { number } status
2015-09-16 11:58:26 -04:00
* @ param { string } message
2014-10-31 06:41:07 -04:00
* /
2015-09-16 11:58:26 -04:00
updateStatus : function ( $tr , status , message ) {
2014-10-31 06:41:07 -04:00
var $statusSpan = $tr . find ( '.status span' ) ;
switch ( status ) {
2015-09-16 11:19:13 -04:00
case null :
// remove status
2024-07-29 06:13:21 -04:00
$statusSpan . hide ( ) ;
2015-09-16 11:19:13 -04:00
break ;
2014-10-31 06:41:07 -04:00
case StorageConfig . Status . IN _PROGRESS :
2018-02-22 07:09:06 -05:00
$statusSpan . attr ( 'class' , 'icon-loading-small' ) ;
2014-10-31 06:41:07 -04:00
break ;
case StorageConfig . Status . SUCCESS :
2018-02-22 07:09:06 -05:00
$statusSpan . attr ( 'class' , 'success icon-checkmark-white' ) ;
2014-10-31 06:41:07 -04:00
break ;
2015-08-12 14:51:09 -04:00
case StorageConfig . Status . INDETERMINATE :
2018-02-22 07:09:06 -05:00
$statusSpan . attr ( 'class' , 'indeterminate icon-info-white' ) ;
2015-08-12 14:51:09 -04:00
break ;
2014-10-31 06:41:07 -04:00
default :
2018-02-22 07:09:06 -05:00
$statusSpan . attr ( 'class' , 'error icon-error-white' ) ;
2014-10-31 06:41:07 -04:00
}
2024-07-29 06:13:21 -04:00
if ( status !== null ) {
$statusSpan . show ( ) ;
}
2024-07-29 06:18:12 -04:00
if ( typeof message !== 'string' ) {
message = t ( 'files_external' , 'Click to recheck the configuration' ) ;
2014-10-31 06:41:07 -04:00
}
2024-07-29 06:18:12 -04:00
$statusSpan . attr ( 'title' , message ) ;
2014-10-31 06:41:07 -04:00
} ,
/ * *
* Suggest mount point name that doesn ' t conflict with the existing names in the list
*
* @ param { string } defaultMountPoint default name
* /
_suggestMountPoint : function ( defaultMountPoint ) {
var $el = this . $el ;
2012-12-28 15:56:48 -05:00
var pos = defaultMountPoint . indexOf ( '/' ) ;
if ( pos !== - 1 ) {
defaultMountPoint = defaultMountPoint . substring ( 0 , pos ) ;
}
defaultMountPoint = defaultMountPoint . replace ( /\s+/g , '' ) ;
2012-08-13 17:09:37 -04:00
var i = 1 ;
var append = '' ;
var match = true ;
while ( match && i < 20 ) {
match = false ;
2014-10-31 06:41:07 -04:00
$el . find ( 'tbody td.mountPoint input' ) . each ( function ( index , mountPoint ) {
2014-08-14 12:48:34 -04:00
if ( $ ( mountPoint ) . val ( ) === defaultMountPoint + append ) {
2012-08-13 17:09:37 -04:00
match = true ;
return false ;
}
} ) ;
if ( match ) {
append = i ;
i ++ ;
} else {
break ;
}
}
2014-10-31 06:41:07 -04:00
return defaultMountPoint + append ;
2015-03-16 09:07:53 -04:00
} ,
2015-09-13 18:23:42 -04:00
2015-03-16 09:07:53 -04:00
/ * *
* Toggles the mount options dropdown
*
* @ param { Object } $tr configuration row
2015-09-13 18:23:42 -04:00
* /
2015-03-16 09:07:53 -04:00
_showMountOptionsDropdown : function ( $tr ) {
var self = this ;
var storage = this . getStorageConfig ( $tr ) ;
var $toggle = $tr . find ( '.mountOptionsToggle' ) ;
var dropDown = new MountOptionsDropdown ( ) ;
2016-05-02 11:34:24 -04:00
var visibleOptions = [
'previews' ,
'filesystem_check_changes' ,
'enable_sharing' ,
2018-02-27 08:06:14 -05:00
'encoding_compatibility' ,
2018-08-01 03:19:52 -04:00
'readonly' ,
'delete'
2016-05-02 11:34:24 -04:00
] ;
2015-03-31 10:25:33 -04:00
if ( this . _encryptionEnabled ) {
2016-05-02 11:34:24 -04:00
visibleOptions . push ( 'encrypt' ) ;
2015-03-31 10:25:33 -04:00
}
2016-05-02 11:34:24 -04:00
dropDown . show ( $toggle , storage . mountOptions || [ ] , visibleOptions ) ;
2015-03-16 09:07:53 -04:00
$ ( 'body' ) . on ( 'mouseup.mountOptionsDropdown' , function ( event ) {
var $target = $ ( event . target ) ;
2018-02-22 06:13:35 -05:00
if ( $target . closest ( '.popovermenu' ) . length ) {
2015-03-16 09:07:53 -04:00
return ;
}
dropDown . hide ( ) ;
} ) ;
dropDown . $el . on ( 'hide' , function ( ) {
var mountOptions = dropDown . getOptions ( ) ;
$ ( 'body' ) . off ( 'mouseup.mountOptionsDropdown' ) ;
$tr . find ( 'input.mountOptions' ) . val ( JSON . stringify ( mountOptions ) ) ;
2022-07-26 07:04:46 -04:00
$tr . find ( 'td.mountOptionsToggle>.icon-more' ) . attr ( 'aria-expanded' , 'false' ) ;
2015-03-16 09:07:53 -04:00
self . saveStorageConfig ( $tr ) ;
} ) ;
2012-08-29 02:42:49 -04:00
}
2015-08-12 17:01:21 -04:00
} , OC . Backbone . Events ) ;
2012-08-13 17:09:37 -04:00
2020-07-20 06:30:35 -04:00
window . addEventListener ( 'DOMContentLoaded' , function ( ) {
2015-04-02 12:17:28 -04:00
var enabled = $ ( '#files_external' ) . attr ( 'data-encryption-enabled' ) ;
2021-12-06 05:18:59 -05:00
var canCreateLocal = $ ( '#files_external' ) . attr ( 'data-can-create-local' ) ;
2015-04-02 12:17:28 -04:00
var encryptionEnabled = ( enabled === 'true' ) ? true : false ;
2015-03-31 10:25:33 -04:00
var mountConfigListView = new MountConfigListView ( $ ( '#externalStorage' ) , {
2021-12-06 05:18:59 -05:00
encryptionEnabled : encryptionEnabled ,
canCreateLocal : ( canCreateLocal === 'true' ) ? true : false ,
2015-03-31 10:25:33 -04:00
} ) ;
2015-09-14 17:21:42 -04:00
mountConfigListView . loadStorages ( ) ;
2012-12-28 17:38:24 -05:00
2014-10-31 06:41:07 -04:00
// TODO: move this into its own View class
2014-10-13 12:40:57 -04:00
var $allowUserMounting = $ ( '#allowUserMounting' ) ;
$allowUserMounting . bind ( 'change' , function ( ) {
2014-02-18 10:36:02 -05:00
OC . msg . startSaving ( '#userMountingMsg' ) ;
2012-06-08 11:42:00 -04:00
if ( this . checked ) {
2016-09-20 05:17:13 -04:00
OCP . AppConfig . setValue ( 'files_external' , 'allow_user_mounting' , 'yes' ) ;
2014-06-13 12:14:41 -04:00
$ ( 'input[name="allowUserMountingBackends\\[\\]"]' ) . prop ( 'checked' , true ) ;
2014-06-16 05:33:51 -04:00
$ ( '#userMountingBackends' ) . removeClass ( 'hidden' ) ;
2014-06-16 05:35:48 -04:00
$ ( 'input[name="allowUserMountingBackends\\[\\]"]' ) . eq ( 0 ) . trigger ( 'change' ) ;
2012-06-08 11:42:00 -04:00
} else {
2016-09-20 05:17:13 -04:00
OCP . AppConfig . setValue ( 'files_external' , 'allow_user_mounting' , 'no' ) ;
2014-06-16 05:33:51 -04:00
$ ( '#userMountingBackends' ) . addClass ( 'hidden' ) ;
2012-06-08 11:42:00 -04:00
}
2014-12-08 15:40:15 -05:00
OC . msg . finishedSaving ( '#userMountingMsg' , { status : 'success' , data : { message : t ( 'files_external' , 'Saved' ) } } ) ;
2012-05-24 11:06:03 -04:00
} ) ;
2014-02-18 10:36:02 -05:00
$ ( 'input[name="allowUserMountingBackends\\[\\]"]' ) . bind ( 'change' , function ( ) {
OC . msg . startSaving ( '#userMountingMsg' ) ;
2015-09-17 12:00:15 -04:00
var userMountingBackends = $ ( 'input[name="allowUserMountingBackends\\[\\]"]:checked' ) . map ( function ( ) {
return $ ( this ) . val ( ) ;
} ) . get ( ) ;
var deprecatedBackends = $ ( 'input[name="allowUserMountingBackends\\[\\]"][data-deprecate-to]' ) . map ( function ( ) {
if ( $ . inArray ( $ ( this ) . data ( 'deprecate-to' ) , userMountingBackends ) !== - 1 ) {
return $ ( this ) . val ( ) ;
}
return null ;
} ) . get ( ) ;
userMountingBackends = userMountingBackends . concat ( deprecatedBackends ) ;
2016-09-20 05:17:13 -04:00
OCP . AppConfig . setValue ( 'files_external' , 'user_mounting_backends' , userMountingBackends . join ( ) ) ;
2014-12-08 15:40:15 -05:00
OC . msg . finishedSaving ( '#userMountingMsg' , { status : 'success' , data : { message : t ( 'files_external' , 'Saved' ) } } ) ;
2014-06-13 12:14:41 -04:00
// disable allowUserMounting
2014-06-16 05:35:48 -04:00
if ( userMountingBackends . length === 0 ) {
2014-10-13 12:40:57 -04:00
$allowUserMounting . prop ( 'checked' , false ) ;
$allowUserMounting . trigger ( 'change' ) ;
2014-06-13 12:14:41 -04:00
}
2014-02-18 10:36:02 -05:00
} ) ;
2014-10-31 06:41:07 -04:00
2016-06-21 07:55:07 -04:00
$ ( '#global_credentials' ) . on ( 'submit' , function ( ) {
var $form = $ ( this ) ;
var uid = $form . find ( '[name=uid]' ) . val ( ) ;
var user = $form . find ( '[name=username]' ) . val ( ) ;
var password = $form . find ( '[name=password]' ) . val ( ) ;
var $submit = $form . find ( '[type=submit]' ) ;
2020-02-05 00:31:42 -05:00
$submit . val ( t ( 'files_external' , 'Saving …' ) ) ;
2016-06-21 07:55:07 -04:00
$ . ajax ( {
type : 'POST' ,
contentType : 'application/json' ,
data : JSON . stringify ( {
uid : uid ,
user : user ,
password : password
} ) ,
url : OC . generateUrl ( 'apps/files_external/globalcredentials' ) ,
dataType : 'json' ,
success : function ( ) {
$submit . val ( t ( 'files_external' , 'Saved' ) ) ;
setTimeout ( function ( ) {
$submit . val ( t ( 'files_external' , 'Save' ) ) ;
} , 2500 ) ;
}
} ) ;
return false ;
} ) ;
2014-10-31 06:41:07 -04:00
// global instance
2018-10-19 11:35:13 -04:00
OCA . Files _External . Settings . mountConfig = mountConfigListView ;
2014-10-31 06:41:07 -04:00
/ * *
* Legacy
*
* @ namespace
2018-10-19 11:35:13 -04:00
* @ deprecated use OCA . Files _External . Settings . mountConfig instead
2014-10-31 06:41:07 -04:00
* /
OC . MountConfig = {
saveStorage : _ . bind ( mountConfigListView . saveStorageConfig , mountConfigListView )
} ;
2012-09-05 16:17:33 -04:00
} ) ;
2013-10-10 05:10:32 -04:00
2014-10-31 06:41:07 -04:00
// export
2018-10-19 11:35:13 -04:00
OCA . Files _External = OCA . Files _External || { } ;
2014-10-31 06:41:07 -04:00
/ * *
* @ namespace
* /
2018-10-19 11:35:13 -04:00
OCA . Files _External . Settings = OCA . Files _External . Settings || { } ;
2014-10-31 06:41:07 -04:00
2018-10-19 11:35:13 -04:00
OCA . Files _External . Settings . GlobalStorageConfig = GlobalStorageConfig ;
OCA . Files _External . Settings . UserStorageConfig = UserStorageConfig ;
OCA . Files _External . Settings . MountConfigListView = MountConfigListView ;
2014-10-31 06:41:07 -04:00
2013-10-10 05:10:32 -04:00
} ) ( ) ;