newfilemenu.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /**
  2. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. /* global Files */
  7. (function() {
  8. /**
  9. * Construct a new NewFileMenu instance
  10. * @constructs NewFileMenu
  11. *
  12. * @memberof OCA.Files
  13. */
  14. var NewFileMenu = OC.Backbone.View.extend({
  15. tagName: 'div',
  16. // Menu is opened by default because it's rendered on "add-button" click
  17. className: 'newFileMenu popovermenu bubble menu open menu-left',
  18. events: {
  19. 'click .menuitem': '_onClickAction'
  20. },
  21. /**
  22. * @type OCA.Files.FileList
  23. */
  24. fileList: null,
  25. initialize: function(options) {
  26. var self = this;
  27. var $uploadEl = $('#file_upload_start');
  28. if ($uploadEl.length) {
  29. $uploadEl.on('fileuploadstart', function() {
  30. self.trigger('actionPerformed', 'upload');
  31. });
  32. } else {
  33. console.warn('Missing upload element "file_upload_start"');
  34. }
  35. this.fileList = options && options.fileList;
  36. this._menuItems = [{
  37. id: 'folder',
  38. displayName: t('files', 'New folder'),
  39. templateName: t('files', 'New folder'),
  40. iconClass: 'icon-folder',
  41. fileType: 'folder',
  42. actionLabel: t('files', 'Create new folder'),
  43. actionHandler: function(name) {
  44. self.fileList.createDirectory(name);
  45. }
  46. }];
  47. OC.Plugins.attach('OCA.Files.NewFileMenu', this);
  48. },
  49. template: function(data) {
  50. return OCA.Files.Templates['newfilemenu'](data);
  51. },
  52. /**
  53. * Event handler whenever an action has been clicked within the menu
  54. *
  55. * @param {Object} event event object
  56. */
  57. _onClickAction: function(event) {
  58. var $target = $(event.target);
  59. if (!$target.hasClass('menuitem')) {
  60. $target = $target.closest('.menuitem');
  61. }
  62. var action = $target.attr('data-action');
  63. // note: clicking the upload label will automatically
  64. // set the focus on the "file_upload_start" hidden field
  65. // which itself triggers the upload dialog.
  66. // Currently the upload logic is still in file-upload.js and filelist.js
  67. if (action === 'upload') {
  68. OC.hideMenus();
  69. } else {
  70. var actionItem = _.filter(this._menuItems, function(item) {
  71. return item.id === action
  72. }).pop();
  73. if (typeof actionItem.useInput === 'undefined' || actionItem.useInput === true) {
  74. event.preventDefault();
  75. this.$el.find('.menuitem.active').removeClass('active');
  76. $target.addClass('active');
  77. this._promptFileName($target);
  78. } else {
  79. actionItem.actionHandler();
  80. OC.hideMenus();
  81. }
  82. }
  83. },
  84. _promptFileName: function($target) {
  85. var self = this;
  86. if ($target.find('form').length) {
  87. $target.find('input[type=\'text\']').focus();
  88. return;
  89. }
  90. // discard other forms
  91. this.$el.find('form').remove();
  92. this.$el.find('.displayname').removeClass('hidden');
  93. $target.find('.displayname').addClass('hidden');
  94. var newName = $target.attr('data-templatename');
  95. var fileType = $target.attr('data-filetype');
  96. var actionLabel = $target.attr('data-action-label');
  97. var $form = $(OCA.Files.Templates['newfilemenu_filename_form']({
  98. fileName: newName,
  99. cid: this.cid,
  100. fileType: fileType,
  101. actionLabel,
  102. }));
  103. //this.trigger('actionPerformed', action);
  104. $target.append($form);
  105. // here comes the OLD code
  106. var $input = $form.find('input[type=\'text\']');
  107. var $submit = $form.find('input[type=\'submit\']');
  108. var lastPos;
  109. var checkInput = function () {
  110. // Special handling for the setup template directory
  111. if ($target.attr('data-action') === 'template-init') {
  112. return true;
  113. }
  114. var filename = $input.val();
  115. try {
  116. if (!Files.isFileNameValid(filename)) {
  117. // Files.isFileNameValid(filename) throws an exception itself
  118. } else if (self.fileList.inList(filename)) {
  119. throw t('files', '{newName} already exists', {newName: filename}, undefined, {
  120. escape: false
  121. });
  122. } else {
  123. return true;
  124. }
  125. } catch (error) {
  126. $input.attr('title', error);
  127. $input.addClass('error');
  128. }
  129. return false;
  130. };
  131. // verify filename on typing
  132. $input.keyup(function() {
  133. if (checkInput()) {
  134. $input.removeClass('error');
  135. }
  136. });
  137. $submit.click(function(event) {
  138. event.stopPropagation();
  139. event.preventDefault();
  140. $form.submit();
  141. });
  142. $input.focus();
  143. // pre select name up to the extension
  144. lastPos = newName.lastIndexOf('.');
  145. if (lastPos === -1) {
  146. lastPos = newName.length;
  147. }
  148. $input.selectRange(0, lastPos);
  149. $form.submit(function(event) {
  150. event.stopPropagation();
  151. event.preventDefault();
  152. if (checkInput()) {
  153. var newname = $input.val().trim();
  154. /* Find the right actionHandler that should be called.
  155. * Actions is retrieved by using `actionSpec.id` */
  156. var action = _.filter(self._menuItems, function(item) {
  157. return item.id == $target.attr('data-action');
  158. }).pop();
  159. action.actionHandler(newname);
  160. $form.remove();
  161. $target.find('.displayname').removeClass('hidden');
  162. OC.hideMenus();
  163. }
  164. });
  165. },
  166. /**
  167. * Add a new item menu entry in the “New” file menu (in
  168. * last position). By clicking on the item, the
  169. * `actionHandler` function is called.
  170. *
  171. * @param {Object} actionSpec item’s properties
  172. */
  173. addMenuEntry: function(actionSpec) {
  174. this._menuItems.push({
  175. id: actionSpec.id,
  176. displayName: actionSpec.displayName,
  177. templateName: actionSpec.templateName,
  178. iconClass: actionSpec.iconClass,
  179. fileType: actionSpec.fileType,
  180. useInput: actionSpec.useInput,
  181. actionLabel: actionSpec.actionLabel,
  182. actionHandler: actionSpec.actionHandler,
  183. checkFilename: actionSpec.checkFilename,
  184. shouldShow: actionSpec.shouldShow,
  185. });
  186. },
  187. /**
  188. * Remove a menu item from the "New" file menu
  189. * @param {string} actionId
  190. */
  191. removeMenuEntry: function(actionId) {
  192. var index = this._menuItems.findIndex(function (actionSpec) {
  193. return actionSpec.id === actionId;
  194. });
  195. if (index > -1) {
  196. this._menuItems.splice(index, 1);
  197. }
  198. },
  199. /**
  200. * Renders the menu with the currently set items
  201. */
  202. render: function() {
  203. const menuItems = this._menuItems.filter(item => !item.shouldShow || (item.shouldShow instanceof Function && item.shouldShow() === true))
  204. this.$el.html(this.template({
  205. uploadMaxHumanFileSize: 'TODO',
  206. uploadLabel: t('files', 'Upload file'),
  207. items: menuItems
  208. }));
  209. // Trigger upload action also with keyboard navigation on enter
  210. this.$el.find('[for="file_upload_start"]').on('keyup', function(event) {
  211. if (event.key === " " || event.key === "Enter") {
  212. $('#file_upload_start').trigger('click');
  213. }
  214. });
  215. },
  216. /**
  217. * Displays the menu under the given element
  218. *
  219. * @param {Object} $target target element
  220. */
  221. showAt: function($target) {
  222. this.render();
  223. OC.showMenu($target, this.$el);
  224. }
  225. });
  226. OCA.Files.NewFileMenu = NewFileMenu;
  227. })();