fileactions.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838
  1. /*
  2. * Copyright (c) 2014
  3. *
  4. * This file is licensed under the Affero General Public License version 3
  5. * or later.
  6. *
  7. * See the COPYING-README file.
  8. *
  9. */
  10. (function() {
  11. /**
  12. * Construct a new FileActions instance
  13. * @constructs FileActions
  14. * @memberof OCA.Files
  15. */
  16. var FileActions = function() {
  17. this.initialize();
  18. };
  19. FileActions.TYPE_DROPDOWN = 0;
  20. FileActions.TYPE_INLINE = 1;
  21. FileActions.prototype = {
  22. /** @lends FileActions.prototype */
  23. actions: {},
  24. defaults: {},
  25. icons: {},
  26. /**
  27. * @deprecated
  28. */
  29. currentFile: null,
  30. /**
  31. * Dummy jquery element, for events
  32. */
  33. $el: null,
  34. _fileActionTriggerTemplate: null,
  35. /**
  36. * @private
  37. */
  38. initialize: function() {
  39. this.clear();
  40. // abusing jquery for events until we get a real event lib
  41. this.$el = $('<div class="dummy-fileactions hidden"></div>');
  42. $('body').append(this.$el);
  43. this._showMenuClosure = _.bind(this._showMenu, this);
  44. },
  45. /**
  46. * Adds an event handler
  47. *
  48. * @param {String} eventName event name
  49. * @param {Function} callback
  50. */
  51. on: function(eventName, callback) {
  52. this.$el.on(eventName, callback);
  53. },
  54. /**
  55. * Removes an event handler
  56. *
  57. * @param {String} eventName event name
  58. * @param {Function} callback
  59. */
  60. off: function(eventName, callback) {
  61. this.$el.off(eventName, callback);
  62. },
  63. /**
  64. * Notifies the event handlers
  65. *
  66. * @param {String} eventName event name
  67. * @param {Object} data data
  68. */
  69. _notifyUpdateListeners: function(eventName, data) {
  70. this.$el.trigger(new $.Event(eventName, data));
  71. },
  72. /**
  73. * Merges the actions from the given fileActions into
  74. * this instance.
  75. *
  76. * @param {OCA.Files.FileActions} fileActions instance of OCA.Files.FileActions
  77. */
  78. merge: function(fileActions) {
  79. var self = this;
  80. // merge first level to avoid unintended overwriting
  81. _.each(fileActions.actions, function(sourceMimeData, mime) {
  82. var targetMimeData = self.actions[mime];
  83. if (!targetMimeData) {
  84. targetMimeData = {};
  85. }
  86. self.actions[mime] = _.extend(targetMimeData, sourceMimeData);
  87. });
  88. this.defaults = _.extend(this.defaults, fileActions.defaults);
  89. this.icons = _.extend(this.icons, fileActions.icons);
  90. },
  91. /**
  92. * @deprecated use #registerAction() instead
  93. */
  94. register: function(mime, name, permissions, icon, action, displayName) {
  95. return this.registerAction({
  96. name: name,
  97. mime: mime,
  98. permissions: permissions,
  99. icon: icon,
  100. actionHandler: action,
  101. displayName: displayName || name
  102. });
  103. },
  104. /**
  105. * Register action
  106. *
  107. * @param {OCA.Files.FileAction} action object
  108. */
  109. registerAction: function (action) {
  110. var mime = action.mime;
  111. var name = action.name;
  112. var actionSpec = {
  113. action: function(fileName, context) {
  114. // Actions registered in one FileAction may be executed on a
  115. // different one (for example, due to the "merge" function),
  116. // so the listeners have to be updated on the FileActions
  117. // from the context instead of on the one in which it was
  118. // originally registered.
  119. if (context && context.fileActions) {
  120. context.fileActions._notifyUpdateListeners('beforeTriggerAction', {action: actionSpec, fileName: fileName, context: context});
  121. }
  122. action.actionHandler(fileName, context);
  123. if (context && context.fileActions) {
  124. context.fileActions._notifyUpdateListeners('afterTriggerAction', {action: actionSpec, fileName: fileName, context: context});
  125. }
  126. },
  127. name: name,
  128. displayName: action.displayName,
  129. mime: mime,
  130. order: action.order || 0,
  131. icon: action.icon,
  132. iconClass: action.iconClass,
  133. permissions: action.permissions,
  134. type: action.type || FileActions.TYPE_DROPDOWN,
  135. altText: action.altText || ''
  136. };
  137. if (_.isUndefined(action.displayName)) {
  138. actionSpec.displayName = t('files', name);
  139. }
  140. if (_.isFunction(action.render)) {
  141. actionSpec.render = action.render;
  142. }
  143. if (!this.actions[mime]) {
  144. this.actions[mime] = {};
  145. }
  146. this.actions[mime][name] = actionSpec;
  147. this.icons[name] = action.icon;
  148. this._notifyUpdateListeners('registerAction', {action: action});
  149. },
  150. /**
  151. * Clears all registered file actions.
  152. */
  153. clear: function() {
  154. this.actions = {};
  155. this.defaults = {};
  156. this.icons = {};
  157. this.currentFile = null;
  158. },
  159. /**
  160. * Sets the default action for a given mime type.
  161. *
  162. * @param {String} mime mime type
  163. * @param {String} name action name
  164. */
  165. setDefault: function (mime, name) {
  166. this.defaults[mime] = name;
  167. this._notifyUpdateListeners('setDefault', {defaultAction: {mime: mime, name: name}});
  168. },
  169. /**
  170. * Returns a map of file actions handlers matching the given conditions
  171. *
  172. * @param {string} mime mime type
  173. * @param {string} type "dir" or "file"
  174. * @param {int} permissions permissions
  175. *
  176. * @return {Object.<string,OCA.Files.FileActions~actionHandler>} map of action name to action spec
  177. */
  178. get: function (mime, type, permissions) {
  179. var actions = this.getActions(mime, type, permissions);
  180. var filteredActions = {};
  181. $.each(actions, function (name, action) {
  182. filteredActions[name] = action.action;
  183. });
  184. return filteredActions;
  185. },
  186. /**
  187. * Returns an array of file actions matching the given conditions
  188. *
  189. * @param {string} mime mime type
  190. * @param {string} type "dir" or "file"
  191. * @param {int} permissions permissions
  192. *
  193. * @return {Array.<OCA.Files.FileAction>} array of action specs
  194. */
  195. getActions: function (mime, type, permissions) {
  196. var actions = {};
  197. if (this.actions.all) {
  198. actions = $.extend(actions, this.actions.all);
  199. }
  200. if (type) {//type is 'dir' or 'file'
  201. if (this.actions[type]) {
  202. actions = $.extend(actions, this.actions[type]);
  203. }
  204. }
  205. if (mime) {
  206. var mimePart = mime.substr(0, mime.indexOf('/'));
  207. if (this.actions[mimePart]) {
  208. actions = $.extend(actions, this.actions[mimePart]);
  209. }
  210. if (this.actions[mime]) {
  211. actions = $.extend(actions, this.actions[mime]);
  212. }
  213. }
  214. var filteredActions = {};
  215. $.each(actions, function (name, action) {
  216. if ((action.permissions === OC.PERMISSION_NONE) || (action.permissions & permissions)) {
  217. filteredActions[name] = action;
  218. }
  219. });
  220. return filteredActions;
  221. },
  222. /**
  223. * Returns the default file action handler for the given conditions
  224. *
  225. * @param {string} mime mime type
  226. * @param {string} type "dir" or "file"
  227. * @param {int} permissions permissions
  228. *
  229. * @return {OCA.Files.FileActions~actionHandler} action handler
  230. *
  231. * @deprecated use getDefaultFileAction instead
  232. */
  233. getDefault: function (mime, type, permissions) {
  234. var defaultActionSpec = this.getDefaultFileAction(mime, type, permissions);
  235. if (defaultActionSpec) {
  236. return defaultActionSpec.action;
  237. }
  238. return undefined;
  239. },
  240. /**
  241. * Returns the default file action handler for the given conditions
  242. *
  243. * @param {string} mime mime type
  244. * @param {string} type "dir" or "file"
  245. * @param {int} permissions permissions
  246. *
  247. * @return {OCA.Files.FileActions~actionHandler} action handler
  248. * @since 8.2
  249. */
  250. getDefaultFileAction: function(mime, type, permissions) {
  251. var mimePart;
  252. if (mime) {
  253. mimePart = mime.substr(0, mime.indexOf('/'));
  254. }
  255. var name = false;
  256. if (mime && this.defaults[mime]) {
  257. name = this.defaults[mime];
  258. } else if (mime && this.defaults[mimePart]) {
  259. name = this.defaults[mimePart];
  260. } else if (type && this.defaults[type]) {
  261. name = this.defaults[type];
  262. } else {
  263. name = this.defaults.all;
  264. }
  265. var actions = this.getActions(mime, type, permissions);
  266. return actions[name];
  267. },
  268. /**
  269. * Default function to render actions
  270. *
  271. * @param {OCA.Files.FileAction} actionSpec file action spec
  272. * @param {boolean} isDefault true if the action is a default one,
  273. * false otherwise
  274. * @param {OCA.Files.FileActionContext} context action context
  275. */
  276. _defaultRenderAction: function(actionSpec, isDefault, context) {
  277. if (!isDefault) {
  278. var params = {
  279. name: actionSpec.name,
  280. nameLowerCase: actionSpec.name.toLowerCase(),
  281. displayName: actionSpec.displayName,
  282. icon: actionSpec.icon,
  283. iconClass: actionSpec.iconClass,
  284. altText: actionSpec.altText,
  285. hasDisplayName: !!actionSpec.displayName
  286. };
  287. if (_.isFunction(actionSpec.icon)) {
  288. params.icon = actionSpec.icon(context.$file.attr('data-file'), context);
  289. }
  290. if (_.isFunction(actionSpec.iconClass)) {
  291. params.iconClass = actionSpec.iconClass(context.$file.attr('data-file'), context);
  292. }
  293. var $actionLink = this._makeActionLink(params, context);
  294. context.$file.find('a.name>span.fileactions').append($actionLink);
  295. $actionLink.addClass('permanent');
  296. return $actionLink;
  297. }
  298. },
  299. /**
  300. * Renders the action link element
  301. *
  302. * @param {Object} params action params
  303. */
  304. _makeActionLink: function(params) {
  305. return $(OCA.Files.Templates['file_action_trigger'](params));
  306. },
  307. /**
  308. * Displays the file actions dropdown menu
  309. *
  310. * @param {string} fileName file name
  311. * @param {OCA.Files.FileActionContext} context rendering context
  312. */
  313. _showMenu: function(fileName, context) {
  314. var menu;
  315. var $trigger = context.$file.closest('tr').find('.fileactions .action-menu');
  316. $trigger.addClass('open');
  317. menu = new OCA.Files.FileActionsMenu();
  318. context.$file.find('td.filename').append(menu.$el);
  319. menu.$el.on('afterHide', function() {
  320. context.$file.removeClass('mouseOver');
  321. $trigger.removeClass('open');
  322. menu.remove();
  323. });
  324. context.$file.addClass('mouseOver');
  325. menu.show(context);
  326. },
  327. /**
  328. * Renders the menu trigger on the given file list row
  329. *
  330. * @param {Object} $tr file list row element
  331. * @param {OCA.Files.FileActionContext} context rendering context
  332. */
  333. _renderMenuTrigger: function($tr, context) {
  334. // remove previous
  335. $tr.find('.action-menu').remove();
  336. var $el = this._renderInlineAction({
  337. name: 'menu',
  338. displayName: '',
  339. iconClass: 'icon-more',
  340. altText: t('files', 'Actions'),
  341. action: this._showMenuClosure
  342. }, false, context);
  343. $el.addClass('permanent');
  344. },
  345. /**
  346. * Renders the action element by calling actionSpec.render() and
  347. * registers the click event to process the action.
  348. *
  349. * @param {OCA.Files.FileAction} actionSpec file action to render
  350. * @param {boolean} isDefault true if the action is a default action,
  351. * false otherwise
  352. * @param {OCA.Files.FileActionContext} context rendering context
  353. */
  354. _renderInlineAction: function(actionSpec, isDefault, context) {
  355. var renderFunc = actionSpec.render || _.bind(this._defaultRenderAction, this);
  356. var $actionEl = renderFunc(actionSpec, isDefault, context);
  357. if (!$actionEl || !$actionEl.length) {
  358. return;
  359. }
  360. $actionEl.on(
  361. 'click', {
  362. a: null
  363. },
  364. function(event) {
  365. event.stopPropagation();
  366. event.preventDefault();
  367. if ($actionEl.hasClass('open')) {
  368. return;
  369. }
  370. var $file = $(event.target).closest('tr');
  371. if ($file.hasClass('busy')) {
  372. return;
  373. }
  374. var currentFile = $file.find('td.filename');
  375. var fileName = $file.attr('data-file');
  376. context.fileActions.currentFile = currentFile;
  377. // also set on global object for legacy apps
  378. window.FileActions.currentFile = currentFile;
  379. var callContext = _.extend({}, context);
  380. if (!context.dir && context.fileList) {
  381. callContext.dir = $file.attr('data-path') || context.fileList.getCurrentDirectory();
  382. }
  383. if (!context.fileInfoModel && context.fileList) {
  384. callContext.fileInfoModel = context.fileList.getModelForFile(fileName);
  385. if (!callContext.fileInfoModel) {
  386. console.warn('No file info model found for file "' + fileName + '"');
  387. }
  388. }
  389. actionSpec.action(
  390. fileName,
  391. callContext
  392. );
  393. }
  394. );
  395. $actionEl.tooltip({placement:'top'});
  396. return $actionEl;
  397. },
  398. /**
  399. * Trigger the given action on the given file.
  400. *
  401. * @param {string} actionName action name
  402. * @param {OCA.Files.FileInfoModel} fileInfoModel file info model
  403. * @param {OCA.Files.FileList} [fileList] file list, for compatibility with older action handlers [DEPRECATED]
  404. *
  405. * @return {boolean} true if the action handler was called, false otherwise
  406. *
  407. * @since 8.2
  408. */
  409. triggerAction: function(actionName, fileInfoModel, fileList) {
  410. var actionFunc;
  411. var actions = this.get(
  412. fileInfoModel.get('mimetype'),
  413. fileInfoModel.isDirectory() ? 'dir' : 'file',
  414. fileInfoModel.get('permissions')
  415. );
  416. if (actionName) {
  417. actionFunc = actions[actionName];
  418. } else {
  419. actionFunc = this.getDefault(
  420. fileInfoModel.get('mimetype'),
  421. fileInfoModel.isDirectory() ? 'dir' : 'file',
  422. fileInfoModel.get('permissions')
  423. );
  424. }
  425. if (!actionFunc) {
  426. actionFunc = actions['Download'];
  427. }
  428. if (!actionFunc) {
  429. return false;
  430. }
  431. var context = {
  432. fileActions: this,
  433. fileInfoModel: fileInfoModel,
  434. dir: fileInfoModel.get('path')
  435. };
  436. var fileName = fileInfoModel.get('name');
  437. this.currentFile = fileName;
  438. // also set on global object for legacy apps
  439. window.FileActions.currentFile = fileName;
  440. if (fileList) {
  441. // compatibility with action handlers that expect these
  442. context.fileList = fileList;
  443. context.$file = fileList.findFileEl(fileName);
  444. }
  445. actionFunc(fileName, context);
  446. },
  447. /**
  448. * Display file actions for the given element
  449. * @param parent "td" element of the file for which to display actions
  450. * @param triggerEvent if true, triggers the fileActionsReady on the file
  451. * list afterwards (false by default)
  452. * @param fileList OCA.Files.FileList instance on which the action is
  453. * done, defaults to OCA.Files.App.fileList
  454. */
  455. display: function (parent, triggerEvent, fileList) {
  456. if (!fileList) {
  457. console.warn('FileActions.display() MUST be called with a OCA.Files.FileList instance');
  458. return;
  459. }
  460. this.currentFile = parent;
  461. var self = this;
  462. var $tr = parent.closest('tr');
  463. var actions = this.getActions(
  464. this.getCurrentMimeType(),
  465. this.getCurrentType(),
  466. this.getCurrentPermissions()
  467. );
  468. var nameLinks;
  469. if ($tr.data('renaming')) {
  470. return;
  471. }
  472. // recreate fileactions container
  473. nameLinks = parent.children('a.name');
  474. nameLinks.find('.fileactions, .nametext .action').remove();
  475. nameLinks.append('<span class="fileactions" />');
  476. var defaultAction = this.getDefaultFileAction(
  477. this.getCurrentMimeType(),
  478. this.getCurrentType(),
  479. this.getCurrentPermissions()
  480. );
  481. var context = {
  482. $file: $tr,
  483. fileActions: this,
  484. fileList: fileList
  485. };
  486. $.each(actions, function (name, actionSpec) {
  487. if (actionSpec.type === FileActions.TYPE_INLINE) {
  488. self._renderInlineAction(
  489. actionSpec,
  490. defaultAction && actionSpec.name === defaultAction.name,
  491. context
  492. );
  493. }
  494. });
  495. function objectValues(obj) {
  496. var res = [];
  497. for (var i in obj) {
  498. if (obj.hasOwnProperty(i)) {
  499. res.push(obj[i]);
  500. }
  501. }
  502. return res;
  503. }
  504. // polyfill
  505. if (!Object.values) {
  506. Object.values = objectValues;
  507. }
  508. var menuActions = Object.values(this.actions.all).filter(function (action) {
  509. return action.type !== OCA.Files.FileActions.TYPE_INLINE;
  510. });
  511. // do not render the menu if nothing is in it
  512. if (menuActions.length > 0) {
  513. this._renderMenuTrigger($tr, context);
  514. }
  515. if (triggerEvent){
  516. fileList.$fileList.trigger(jQuery.Event("fileActionsReady", {fileList: fileList, $files: $tr}));
  517. }
  518. },
  519. getCurrentFile: function () {
  520. return this.currentFile.parent().attr('data-file');
  521. },
  522. getCurrentMimeType: function () {
  523. return this.currentFile.parent().attr('data-mime');
  524. },
  525. getCurrentType: function () {
  526. return this.currentFile.parent().attr('data-type');
  527. },
  528. getCurrentPermissions: function () {
  529. return this.currentFile.parent().data('permissions');
  530. },
  531. /**
  532. * Register the actions that are used by default for the files app.
  533. */
  534. registerDefaultActions: function() {
  535. this.registerAction({
  536. name: 'Download',
  537. displayName: t('files', 'Download'),
  538. order: -20,
  539. mime: 'all',
  540. permissions: OC.PERMISSION_READ,
  541. iconClass: 'icon-download',
  542. actionHandler: function (filename, context) {
  543. var dir = context.dir || context.fileList.getCurrentDirectory();
  544. var isDir = context.$file.attr('data-type') === 'dir';
  545. var url = context.fileList.getDownloadUrl(filename, dir, isDir);
  546. var downloadFileaction = $(context.$file).find('.fileactions .action-download');
  547. // don't allow a second click on the download action
  548. if(downloadFileaction.hasClass('disabled')) {
  549. return;
  550. }
  551. if (url) {
  552. var disableLoadingState = function() {
  553. context.fileList.showFileBusyState(filename, false);
  554. };
  555. context.fileList.showFileBusyState(filename, true);
  556. OCA.Files.Files.handleDownload(url, disableLoadingState);
  557. }
  558. }
  559. });
  560. this.registerAction({
  561. name: 'Rename',
  562. displayName: t('files', 'Rename'),
  563. mime: 'all',
  564. order: -30,
  565. permissions: OC.PERMISSION_UPDATE,
  566. iconClass: 'icon-rename',
  567. actionHandler: function (filename, context) {
  568. context.fileList.rename(filename);
  569. }
  570. });
  571. this.registerAction({
  572. name: 'MoveCopy',
  573. displayName: function(context) {
  574. var permissions = context.fileInfoModel.attributes.permissions;
  575. if (permissions & OC.PERMISSION_UPDATE) {
  576. return t('files', 'Move or copy');
  577. }
  578. return t('files', 'Copy');
  579. },
  580. mime: 'all',
  581. order: -25,
  582. permissions: $('#isPublic').val() ? OC.PERMISSION_UPDATE : OC.PERMISSION_READ,
  583. iconClass: 'icon-external',
  584. actionHandler: function (filename, context) {
  585. var permissions = context.fileInfoModel.attributes.permissions;
  586. var actions = OC.dialogs.FILEPICKER_TYPE_COPY;
  587. if (permissions & OC.PERMISSION_UPDATE) {
  588. actions = OC.dialogs.FILEPICKER_TYPE_COPY_MOVE;
  589. }
  590. var dialogDir = context.dir;
  591. if (typeof context.fileList.dirInfo.dirLastCopiedTo !== 'undefined') {
  592. dialogDir = context.fileList.dirInfo.dirLastCopiedTo;
  593. }
  594. OC.dialogs.filepicker(t('files', 'Choose target folder'), function(targetPath, type) {
  595. if (type === OC.dialogs.FILEPICKER_TYPE_COPY) {
  596. context.fileList.copy(filename, targetPath, false, context.dir);
  597. }
  598. if (type === OC.dialogs.FILEPICKER_TYPE_MOVE) {
  599. context.fileList.move(filename, targetPath, false, context.dir);
  600. }
  601. context.fileList.dirInfo.dirLastCopiedTo = targetPath;
  602. }, false, "httpd/unix-directory", true, actions, dialogDir);
  603. }
  604. });
  605. this.registerAction({
  606. name: 'Open',
  607. mime: 'dir',
  608. permissions: OC.PERMISSION_READ,
  609. icon: '',
  610. actionHandler: function (filename, context) {
  611. var dir = context.$file.attr('data-path') || context.fileList.getCurrentDirectory();
  612. if (OCA.Files.App && OCA.Files.App.getActiveView() !== 'files') {
  613. OCA.Files.App.setActiveView('files', {silent: true});
  614. OCA.Files.App.fileList.changeDirectory(OC.joinPaths(dir, filename), true, true);
  615. } else {
  616. context.fileList.changeDirectory(OC.joinPaths(dir, filename), true, false, parseInt(context.$file.attr('data-id'), 10));
  617. }
  618. },
  619. displayName: t('files', 'Open')
  620. });
  621. this.registerAction({
  622. name: 'Delete',
  623. displayName: function(context) {
  624. var mountType = context.$file.attr('data-mounttype');
  625. var type = context.$file.attr('data-type');
  626. var deleteTitle = (type && type === 'file')
  627. ? t('files', 'Delete file')
  628. : t('files', 'Delete folder')
  629. if (mountType === 'external-root') {
  630. deleteTitle = t('files', 'Disconnect storage');
  631. } else if (mountType === 'shared-root') {
  632. deleteTitle = t('files', 'Unshare');
  633. }
  634. return deleteTitle;
  635. },
  636. mime: 'all',
  637. order: 1000,
  638. // permission is READ because we show a hint instead if there is no permission
  639. permissions: OC.PERMISSION_DELETE,
  640. iconClass: 'icon-delete',
  641. actionHandler: function(fileName, context) {
  642. // if there is no permission to delete do nothing
  643. if((context.$file.data('permissions') & OC.PERMISSION_DELETE) === 0) {
  644. return;
  645. }
  646. context.fileList.do_delete(fileName, context.dir);
  647. $('.tipsy').remove();
  648. }
  649. });
  650. this.setDefault('dir', 'Open');
  651. }
  652. };
  653. OCA.Files.FileActions = FileActions;
  654. /**
  655. * Replaces the button icon with a loading spinner and vice versa
  656. * - also adds the class disabled to the passed in element
  657. *
  658. * @param {jQuery} $buttonElement The button element
  659. * @param {boolean} showIt whether to show the spinner(true) or to hide it(false)
  660. */
  661. OCA.Files.FileActions.updateFileActionSpinner = function($buttonElement, showIt) {
  662. var $icon = $buttonElement.find('.icon');
  663. if (showIt) {
  664. var $loadingIcon = $('<span class="icon icon-loading-small"></span>');
  665. $icon.after($loadingIcon);
  666. $icon.addClass('hidden');
  667. } else {
  668. $buttonElement.find('.icon-loading-small').remove();
  669. $buttonElement.find('.icon').removeClass('hidden');
  670. }
  671. };
  672. /**
  673. * File action attributes.
  674. *
  675. * @todo make this a real class in the future
  676. * @typedef {Object} OCA.Files.FileAction
  677. *
  678. * @property {String} name identifier of the action
  679. * @property {(String|OCA.Files.FileActions~displayNameFunction)} displayName
  680. * display name string for the action, or function that returns the display name.
  681. * Defaults to the name given in name property
  682. * @property {String} mime mime type
  683. * @property {int} permissions permissions
  684. * @property {(Function|String)} icon icon path to the icon or function that returns it (deprecated, use iconClass instead)
  685. * @property {(String|OCA.Files.FileActions~iconClassFunction)} iconClass class name of the icon (recommended for theming)
  686. * @property {OCA.Files.FileActions~renderActionFunction} [render] optional rendering function
  687. * @property {OCA.Files.FileActions~actionHandler} actionHandler action handler function
  688. */
  689. /**
  690. * File action context attributes.
  691. *
  692. * @typedef {Object} OCA.Files.FileActionContext
  693. *
  694. * @property {Object} $file jQuery file row element
  695. * @property {OCA.Files.FileActions} fileActions file actions object
  696. * @property {OCA.Files.FileList} fileList file list object
  697. */
  698. /**
  699. * Render function for actions.
  700. * The function must render a link element somewhere in the DOM
  701. * and return it. The function should NOT register the event handler
  702. * as this will be done after the link was returned.
  703. *
  704. * @callback OCA.Files.FileActions~renderActionFunction
  705. * @param {OCA.Files.FileAction} actionSpec action definition
  706. * @param {Object} $row row container
  707. * @param {boolean} isDefault true if the action is the default one,
  708. * false otherwise
  709. * @return {Object} jQuery link object
  710. */
  711. /**
  712. * Display name function for actions.
  713. * The function returns the display name of the action using
  714. * the given context information..
  715. *
  716. * @callback OCA.Files.FileActions~displayNameFunction
  717. * @param {OCA.Files.FileActionContext} context action context
  718. * @return {String} display name
  719. */
  720. /**
  721. * Icon class function for actions.
  722. * The function returns the icon class of the action using
  723. * the given context information.
  724. *
  725. * @callback OCA.Files.FileActions~iconClassFunction
  726. * @param {String} fileName name of the file on which the action must be performed
  727. * @param {OCA.Files.FileActionContext} context action context
  728. * @return {String} icon class
  729. */
  730. /**
  731. * Action handler function for file actions
  732. *
  733. * @callback OCA.Files.FileActions~actionHandler
  734. * @param {String} fileName name of the file on which the action must be performed
  735. * @param context context
  736. * @param {String} context.dir directory of the file
  737. * @param {OCA.Files.FileInfoModel} fileInfoModel file info model
  738. * @param {Object} [context.$file] jQuery element of the file [DEPRECATED]
  739. * @param {OCA.Files.FileList} [context.fileList] the FileList instance on which the action occurred [DEPRECATED]
  740. * @param {OCA.Files.FileActions} context.fileActions the FileActions instance on which the action occurred
  741. */
  742. // global file actions to be used by all lists
  743. OCA.Files.fileActions = new OCA.Files.FileActions();
  744. OCA.Files.legacyFileActions = new OCA.Files.FileActions();
  745. // for backward compatibility
  746. //
  747. // legacy apps are expecting a stateful global FileActions object to register
  748. // their actions on. Since legacy apps are very likely to break with other
  749. // FileList views than the main one ("All files"), actions registered
  750. // through window.FileActions will be limited to the main file list.
  751. // @deprecated use OCA.Files.FileActions instead
  752. window.FileActions = OCA.Files.legacyFileActions;
  753. window.FileActions.register = function (mime, name, permissions, icon, action, displayName) {
  754. console.warn('FileActions.register() is deprecated, please use OCA.Files.fileActions.register() instead', arguments);
  755. OCA.Files.FileActions.prototype.register.call(
  756. window.FileActions, mime, name, permissions, icon, action, displayName
  757. );
  758. };
  759. window.FileActions.display = function (parent, triggerEvent, fileList) {
  760. fileList = fileList || OCA.Files.App.fileList;
  761. console.warn('FileActions.display() is deprecated, please use OCA.Files.fileActions.register() which automatically redisplays actions', mime, name);
  762. OCA.Files.FileActions.prototype.display.call(window.FileActions, parent, triggerEvent, fileList);
  763. };
  764. })();