|
|
|
@ -8,7 +8,6 @@ |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/* global trashBinApp */ |
|
|
|
|
(function() { |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -109,33 +108,37 @@ |
|
|
|
|
permissions: permissions, |
|
|
|
|
icon: icon, |
|
|
|
|
actionHandler: action, |
|
|
|
|
displayName: displayName |
|
|
|
|
displayName: displayName || name |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
/** |
|
|
|
|
* Register action |
|
|
|
|
* |
|
|
|
|
* @param {Object} action action object |
|
|
|
|
* @param {String} action.name identifier of the action |
|
|
|
|
* @param {String} action.displayName display name of the action, defaults |
|
|
|
|
* to the name given in action.name |
|
|
|
|
* @param {String} action.mime mime type |
|
|
|
|
* @param {int} action.permissions permissions |
|
|
|
|
* @param {(Function|String)} action.icon icon path to the icon or function |
|
|
|
|
* that returns it |
|
|
|
|
* @param {OCA.Files.FileActions~actionHandler} action.actionHandler action handler function |
|
|
|
|
* @param {OCA.Files.FileAction} action object |
|
|
|
|
*/ |
|
|
|
|
registerAction: function (action) { |
|
|
|
|
var mime = action.mime; |
|
|
|
|
var name = action.name; |
|
|
|
|
var actionSpec = { |
|
|
|
|
action: action.actionHandler, |
|
|
|
|
name: name, |
|
|
|
|
displayName: action.displayName, |
|
|
|
|
mime: mime, |
|
|
|
|
icon: action.icon, |
|
|
|
|
permissions: action.permissions |
|
|
|
|
}; |
|
|
|
|
if (_.isUndefined(action.displayName)) { |
|
|
|
|
actionSpec.displayName = t('files', name); |
|
|
|
|
} |
|
|
|
|
if (_.isFunction(action.render)) { |
|
|
|
|
actionSpec.render = action.render; |
|
|
|
|
} else { |
|
|
|
|
actionSpec.render = _.bind(this._defaultRenderAction, this); |
|
|
|
|
} |
|
|
|
|
if (!this.actions[mime]) { |
|
|
|
|
this.actions[mime] = {}; |
|
|
|
|
} |
|
|
|
|
this.actions[mime][name] = { |
|
|
|
|
action: action.actionHandler, |
|
|
|
|
permissions: action.permissions, |
|
|
|
|
displayName: action.displayName || t('files', name) |
|
|
|
|
}; |
|
|
|
|
this.actions[mime][name] = actionSpec; |
|
|
|
|
this.icons[name] = action.icon; |
|
|
|
|
this._notifyUpdateListeners('registerAction', {action: action}); |
|
|
|
|
}, |
|
|
|
@ -212,6 +215,127 @@ |
|
|
|
|
var actions = this.get(mime, type, permissions); |
|
|
|
|
return actions[name]; |
|
|
|
|
}, |
|
|
|
|
/** |
|
|
|
|
* Default function to render actions |
|
|
|
|
* |
|
|
|
|
* @param {OCA.Files.FileAction} actionSpec file action spec |
|
|
|
|
* @param {boolean} isDefault true if the action is a default one, |
|
|
|
|
* false otherwise |
|
|
|
|
* @param {OCA.Files.FileActionContext} context action context |
|
|
|
|
*/ |
|
|
|
|
_defaultRenderAction: function(actionSpec, isDefault, context) { |
|
|
|
|
var name = actionSpec.name; |
|
|
|
|
if (name === 'Download' || !isDefault) { |
|
|
|
|
var $actionLink = this._makeActionLink(actionSpec, context); |
|
|
|
|
context.$file.find('a.name>span.fileactions').append($actionLink); |
|
|
|
|
return $actionLink; |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
/** |
|
|
|
|
* Renders the action link element |
|
|
|
|
* |
|
|
|
|
* @param {OCA.Files.FileAction} actionSpec action object |
|
|
|
|
* @param {OCA.Files.FileActionContext} context action context |
|
|
|
|
*/ |
|
|
|
|
_makeActionLink: function(actionSpec, context) { |
|
|
|
|
var img = actionSpec.icon; |
|
|
|
|
if (img && img.call) { |
|
|
|
|
img = img(context.$file.attr('data-file')); |
|
|
|
|
} |
|
|
|
|
var html = '<a href="#">'; |
|
|
|
|
if (img) { |
|
|
|
|
html += '<img class="svg" alt="" src="' + img + '" />'; |
|
|
|
|
} |
|
|
|
|
if (actionSpec.displayName) { |
|
|
|
|
html += '<span> ' + actionSpec.displayName + '</span></a>'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $(html); |
|
|
|
|
}, |
|
|
|
|
/** |
|
|
|
|
* Custom renderer for the "Rename" action. |
|
|
|
|
* Displays the rename action as an icon behind the file name. |
|
|
|
|
* |
|
|
|
|
* @param {OCA.Files.FileAction} actionSpec file action to render |
|
|
|
|
* @param {boolean} isDefault true if the action is a default action, |
|
|
|
|
* false otherwise |
|
|
|
|
* @param {OCAFiles.FileActionContext} context rendering context |
|
|
|
|
*/ |
|
|
|
|
_renderRenameAction: function(actionSpec, isDefault, context) { |
|
|
|
|
var $actionEl = this._makeActionLink(actionSpec, context); |
|
|
|
|
var $container = context.$file.find('a.name span.nametext'); |
|
|
|
|
$container.find('.action-rename').remove(); |
|
|
|
|
$container.append($actionEl); |
|
|
|
|
return $actionEl; |
|
|
|
|
}, |
|
|
|
|
/** |
|
|
|
|
* Custom renderer for the "Delete" action. |
|
|
|
|
* Displays the "Delete" action as a trash icon at the end of |
|
|
|
|
* the table row. |
|
|
|
|
* |
|
|
|
|
* @param {OCA.Files.FileAction} actionSpec file action to render |
|
|
|
|
* @param {boolean} isDefault true if the action is a default action, |
|
|
|
|
* false otherwise |
|
|
|
|
* @param {OCAFiles.FileActionContext} context rendering context |
|
|
|
|
*/ |
|
|
|
|
_renderDeleteAction: function(actionSpec, isDefault, context) { |
|
|
|
|
var mountType = context.$file.attr('data-mounttype'); |
|
|
|
|
var deleteTitle = t('files', 'Delete'); |
|
|
|
|
if (mountType === 'external-root') { |
|
|
|
|
deleteTitle = t('files', 'Disconnect storage'); |
|
|
|
|
} else if (mountType === 'shared-root') { |
|
|
|
|
deleteTitle = t('files', 'Unshare'); |
|
|
|
|
} |
|
|
|
|
var $actionLink = $('<a href="#" original-title="' + |
|
|
|
|
escapeHTML(deleteTitle) + |
|
|
|
|
'" class="action delete icon-delete" />' |
|
|
|
|
); |
|
|
|
|
var $container = context.$file.find('td:last'); |
|
|
|
|
$container.find('.delete').remove(); |
|
|
|
|
$container.append($actionLink); |
|
|
|
|
return $actionLink; |
|
|
|
|
}, |
|
|
|
|
/** |
|
|
|
|
* Renders the action element by calling actionSpec.render() and |
|
|
|
|
* registers the click event to process the action. |
|
|
|
|
* |
|
|
|
|
* @param {OCA.Files.FileAction} actionSpec file action to render |
|
|
|
|
* @param {boolean} isDefault true if the action is a default action, |
|
|
|
|
* false otherwise |
|
|
|
|
* @param {OCAFiles.FileActionContext} context rendering context |
|
|
|
|
*/ |
|
|
|
|
_renderAction: function(actionSpec, isDefault, context) { |
|
|
|
|
var $actionEl = actionSpec.render(actionSpec, isDefault, context); |
|
|
|
|
if (!$actionEl || !$actionEl.length) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
$actionEl.addClass('action action-' + actionSpec.name.toLowerCase()); |
|
|
|
|
$actionEl.attr('data-action', actionSpec.name); |
|
|
|
|
$actionEl.on( |
|
|
|
|
'click', { |
|
|
|
|
a: null |
|
|
|
|
}, |
|
|
|
|
function(event) { |
|
|
|
|
var $file = $(event.target).closest('tr'); |
|
|
|
|
var currentFile = $file.find('td.filename'); |
|
|
|
|
var fileName = $file.attr('data-file'); |
|
|
|
|
event.stopPropagation(); |
|
|
|
|
event.preventDefault(); |
|
|
|
|
|
|
|
|
|
context.fileActions.currentFile = currentFile; |
|
|
|
|
// also set on global object for legacy apps
|
|
|
|
|
window.FileActions.currentFile = currentFile; |
|
|
|
|
|
|
|
|
|
actionSpec.action( |
|
|
|
|
fileName, |
|
|
|
|
_.extend(context, { |
|
|
|
|
dir: $file.attr('data-path') || context.fileList.getCurrentDirectory() |
|
|
|
|
}) |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
return $actionEl; |
|
|
|
|
}, |
|
|
|
|
/** |
|
|
|
|
* Display file actions for the given element |
|
|
|
|
* @param parent "td" element of the file for which to display actions |
|
|
|
@ -226,107 +350,51 @@ |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
this.currentFile = parent; |
|
|
|
|
var $tr = parent.closest('tr'); |
|
|
|
|
var self = this; |
|
|
|
|
var actions = this.getActions(this.getCurrentMimeType(), this.getCurrentType(), this.getCurrentPermissions()); |
|
|
|
|
var file = this.getCurrentFile(); |
|
|
|
|
var $tr = parent.closest('tr'); |
|
|
|
|
var actions = this.getActions( |
|
|
|
|
this.getCurrentMimeType(), |
|
|
|
|
this.getCurrentType(), |
|
|
|
|
this.getCurrentPermissions() |
|
|
|
|
); |
|
|
|
|
var nameLinks; |
|
|
|
|
if ($tr.data('renaming')) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// recreate fileactions
|
|
|
|
|
// recreate fileactions container
|
|
|
|
|
nameLinks = parent.children('a.name'); |
|
|
|
|
nameLinks.find('.fileactions, .nametext .action').remove(); |
|
|
|
|
nameLinks.append('<span class="fileactions" />'); |
|
|
|
|
var defaultAction = this.getDefault(this.getCurrentMimeType(), this.getCurrentType(), this.getCurrentPermissions()); |
|
|
|
|
|
|
|
|
|
var actionHandler = function (event) { |
|
|
|
|
event.stopPropagation(); |
|
|
|
|
event.preventDefault(); |
|
|
|
|
var defaultAction = this.getDefault( |
|
|
|
|
this.getCurrentMimeType(), |
|
|
|
|
this.getCurrentType(), |
|
|
|
|
this.getCurrentPermissions() |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
self.currentFile = event.data.elem; |
|
|
|
|
// also set on global object for legacy apps
|
|
|
|
|
window.FileActions.currentFile = self.currentFile; |
|
|
|
|
|
|
|
|
|
var file = self.getCurrentFile(); |
|
|
|
|
var $tr = $(this).closest('tr'); |
|
|
|
|
|
|
|
|
|
event.data.actionFunc(file, { |
|
|
|
|
$file: $tr, |
|
|
|
|
fileList: fileList, |
|
|
|
|
fileActions: self, |
|
|
|
|
dir: $tr.attr('data-path') || fileList.getCurrentDirectory() |
|
|
|
|
}); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
var addAction = function (name, action, displayName) { |
|
|
|
|
|
|
|
|
|
if ((name === 'Download' || action !== defaultAction) && name !== 'Delete') { |
|
|
|
|
|
|
|
|
|
var img = self.icons[name], |
|
|
|
|
actionText = displayName, |
|
|
|
|
actionContainer = 'a.name>span.fileactions'; |
|
|
|
|
|
|
|
|
|
if (name === 'Rename') { |
|
|
|
|
// rename has only an icon which appears behind
|
|
|
|
|
// the file name
|
|
|
|
|
actionText = ''; |
|
|
|
|
actionContainer = 'a.name span.nametext'; |
|
|
|
|
} |
|
|
|
|
if (img.call) { |
|
|
|
|
img = img(file); |
|
|
|
|
} |
|
|
|
|
var html = '<a href="#" class="action action-' + name.toLowerCase() + '" data-action="' + name + '">'; |
|
|
|
|
if (img) { |
|
|
|
|
html += '<img class ="svg" alt="" src="' + img + '" />'; |
|
|
|
|
} |
|
|
|
|
html += '<span> ' + actionText + '</span></a>'; |
|
|
|
|
|
|
|
|
|
var element = $(html); |
|
|
|
|
element.data('action', name); |
|
|
|
|
element.on('click', {a: null, elem: parent, actionFunc: actions[name].action}, actionHandler); |
|
|
|
|
parent.find(actionContainer).append(element); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
$.each(actions, function (name, action) { |
|
|
|
|
$.each(actions, function (name, actionSpec) { |
|
|
|
|
if (name !== 'Share') { |
|
|
|
|
displayName = action.displayName; |
|
|
|
|
ah = action.action; |
|
|
|
|
|
|
|
|
|
addAction(name, ah, displayName); |
|
|
|
|
self._renderAction( |
|
|
|
|
actionSpec, |
|
|
|
|
actionSpec.action === defaultAction, { |
|
|
|
|
$file: $tr, |
|
|
|
|
fileActions: this, |
|
|
|
|
fileList : fileList |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
if(actions.Share){ |
|
|
|
|
displayName = t('files', 'Share'); |
|
|
|
|
addAction('Share', actions.Share, displayName); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// remove the existing delete action
|
|
|
|
|
parent.parent().children().last().find('.action.delete').remove(); |
|
|
|
|
if (actions['Delete']) { |
|
|
|
|
var img = self.icons['Delete']; |
|
|
|
|
var html; |
|
|
|
|
var mountType = $tr.attr('data-mounttype'); |
|
|
|
|
var deleteTitle = t('files', 'Delete'); |
|
|
|
|
if (mountType === 'external-root') { |
|
|
|
|
deleteTitle = t('files', 'Disconnect storage'); |
|
|
|
|
} else if (mountType === 'shared-root') { |
|
|
|
|
deleteTitle = t('files', 'Unshare'); |
|
|
|
|
} else if (fileList.id === 'trashbin') { |
|
|
|
|
deleteTitle = t('files', 'Delete permanently'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (img.call) { |
|
|
|
|
img = img(file); |
|
|
|
|
} |
|
|
|
|
html = '<a href="#" original-title="' + escapeHTML(deleteTitle) + '" class="action delete icon-delete" />'; |
|
|
|
|
var element = $(html); |
|
|
|
|
element.data('action', actions['Delete']); |
|
|
|
|
element.on('click', {a: null, elem: parent, actionFunc: actions['Delete'].action}, actionHandler); |
|
|
|
|
parent.parent().children().last().append(element); |
|
|
|
|
// added here to make sure it's always the last action
|
|
|
|
|
var shareActionSpec = actions.Share; |
|
|
|
|
if (shareActionSpec){ |
|
|
|
|
this._renderAction( |
|
|
|
|
shareActionSpec, |
|
|
|
|
shareActionSpec.action === defaultAction, { |
|
|
|
|
$file: $tr, |
|
|
|
|
fileActions: this, |
|
|
|
|
fileList: fileList |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (triggerEvent){ |
|
|
|
@ -350,18 +418,34 @@ |
|
|
|
|
* Register the actions that are used by default for the files app. |
|
|
|
|
*/ |
|
|
|
|
registerDefaultActions: function() { |
|
|
|
|
this.register('all', 'Delete', OC.PERMISSION_DELETE, function () { |
|
|
|
|
return OC.imagePath('core', 'actions/delete'); |
|
|
|
|
}, function (filename, context) { |
|
|
|
|
context.fileList.do_delete(filename, context.dir); |
|
|
|
|
$('.tipsy').remove(); |
|
|
|
|
this.registerAction({ |
|
|
|
|
name: 'Delete', |
|
|
|
|
displayName: '', |
|
|
|
|
mime: 'all', |
|
|
|
|
permissions: OC.PERMISSION_DELETE, |
|
|
|
|
icon: function() { |
|
|
|
|
return OC.imagePath('core', 'actions/delete'); |
|
|
|
|
}, |
|
|
|
|
render: _.bind(this._renderDeleteAction, this), |
|
|
|
|
actionHandler: function(fileName, context) { |
|
|
|
|
context.fileList.do_delete(fileName, context.dir); |
|
|
|
|
$('.tipsy').remove(); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// t('files', 'Rename')
|
|
|
|
|
this.register('all', 'Rename', OC.PERMISSION_UPDATE, function () { |
|
|
|
|
return OC.imagePath('core', 'actions/rename'); |
|
|
|
|
}, function (filename, context) { |
|
|
|
|
context.fileList.rename(filename); |
|
|
|
|
this.registerAction({ |
|
|
|
|
name: 'Rename', |
|
|
|
|
displayName: '', |
|
|
|
|
mime: 'all', |
|
|
|
|
permissions: OC.PERMISSION_UPDATE, |
|
|
|
|
icon: function() { |
|
|
|
|
return OC.imagePath('core', 'actions/rename'); |
|
|
|
|
}, |
|
|
|
|
render: _.bind(this._renderRenameAction, this), |
|
|
|
|
actionHandler: function (filename, context) { |
|
|
|
|
context.fileList.rename(filename); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { |
|
|
|
@ -388,6 +472,47 @@ |
|
|
|
|
|
|
|
|
|
OCA.Files.FileActions = FileActions; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* File action attributes. |
|
|
|
|
* |
|
|
|
|
* @todo make this a real class in the future |
|
|
|
|
* @typedef {Object} OCA.Files.FileAction |
|
|
|
|
* |
|
|
|
|
* @property {String} name identifier of the action |
|
|
|
|
* @property {String} displayName display name of the action, defaults |
|
|
|
|
* to the name given in name property |
|
|
|
|
* @property {String} mime mime type |
|
|
|
|
* @property {int} permissions permissions |
|
|
|
|
* @property {(Function|String)} icon icon path to the icon or function |
|
|
|
|
* that returns it |
|
|
|
|
* @property {OCA.Files.FileActions~renderActionFunction} [render] optional rendering function |
|
|
|
|
* @property {OCA.Files.FileActions~actionHandler} actionHandler action handler function |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* File action context attributes. |
|
|
|
|
* |
|
|
|
|
* @typedef {Object} OCA.Files.FileActionContext |
|
|
|
|
* |
|
|
|
|
* @property {Object} $file jQuery file row element |
|
|
|
|
* @property {OCA.Files.FileActions} fileActions file actions object |
|
|
|
|
* @property {OCA.Files.FileList} fileList file list object |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Render function for actions. |
|
|
|
|
* The function must render a link element somewhere in the DOM |
|
|
|
|
* and return it. The function should NOT register the event handler |
|
|
|
|
* as this will be done after the link was returned. |
|
|
|
|
* |
|
|
|
|
* @callback OCA.Files.FileActions~renderActionFunction |
|
|
|
|
* @param {OCA.Files.FileAction} actionSpec action definition |
|
|
|
|
* @param {Object} $row row container |
|
|
|
|
* @param {boolean} isDefault true if the action is the default one, |
|
|
|
|
* false otherwise |
|
|
|
|
* @return {Object} jQuery link object |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Action handler function for file actions |
|
|
|
|
* |
|
|
|
|