Toolbar.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /*
  2. * Copyright (C) 2015 - present Instructure, Inc.
  3. *
  4. * This file is part of Canvas.
  5. *
  6. * Canvas is free software: you can redistribute it and/or modify it under
  7. * the terms of the GNU Affero General Public License as published by the Free
  8. * Software Foundation, version 3 of the License.
  9. *
  10. * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
  11. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  13. * details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License along
  16. * with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. import $ from 'jquery'
  19. import I18n from 'i18n!react_files'
  20. import React from 'react'
  21. import ReactDOM from 'react-dom'
  22. import page from 'page'
  23. import Toolbar from 'compiled/react_files/components/Toolbar'
  24. import FocusStore from 'compiled/react_files/modules/FocusStore'
  25. import openMoveDialog from 'jsx/files/utils/openMoveDialog'
  26. import deleteStuff from 'compiled/react_files/utils/deleteStuff'
  27. import UploadButton from 'jsx/files/UploadButton'
  28. import classnames from 'classnames'
  29. import preventDefault from 'compiled/fn/preventDefault'
  30. import Folder from 'compiled/models/Folder'
  31. Toolbar.openPreview = function () {
  32. FocusStore.setItemToFocus(ReactDOM.findDOMNode(this.refs.previewLink));
  33. const queryString = $.param(this.props.getPreviewQuery());
  34. page(`${this.props.getPreviewRoute()}?${queryString}`);
  35. };
  36. Toolbar.onSubmitSearch = function (event) {
  37. event.preventDefault();
  38. const searchTerm = ReactDOM.findDOMNode(this.refs.searchTerm).value;
  39. page(`/search?search_term=${searchTerm}`);
  40. };
  41. Toolbar.renderUploadAddFolderButtons = function (canManage) {
  42. var phoneHiddenSet = classnames({
  43. 'hidden-phone' : this.showingButtons
  44. });
  45. if (canManage) {
  46. return (
  47. <div className='ef-actions'>
  48. <button
  49. type= 'button'
  50. onClick= {this.addFolder}
  51. className='btn btn-add-folder'
  52. aria-label= {I18n.t('Add Folder')}
  53. >
  54. <i className='icon-plus' />&nbsp;
  55. <span className= {phoneHiddenSet} >
  56. {I18n.t('Folder')}
  57. </span>
  58. </button>
  59. <UploadButton
  60. currentFolder= {this.props.currentFolder}
  61. showingButtons= {this.showingButtons}
  62. contextId= {this.props.contextId}
  63. contextType= {this.props.contextType}
  64. />
  65. </div>
  66. );
  67. }
  68. }
  69. Toolbar.renderDeleteButton = function (canManage) {
  70. if (canManage) {
  71. return (
  72. <button
  73. type= 'button'
  74. disabled= {!this.showingButtons}
  75. className= 'ui-button btn-delete'
  76. onClick= { function () {
  77. this.props.clearSelectedItems()
  78. deleteStuff(this.props.selectedItems)
  79. }.bind(this)
  80. }
  81. title= {I18n.t('Delete')}
  82. aria-label= {I18n.t('Delete')}
  83. data-tooltip=""
  84. >
  85. <i className='icon-trash' />
  86. </button>
  87. );
  88. }
  89. }
  90. Toolbar.renderManageUsageRightsButton = function () {
  91. if (this.props.userCanManageFilesForContext && this.props.usageRightsRequiredForContext) {
  92. return (
  93. <button
  94. ref= 'usageRightsBtn'
  95. type= 'button'
  96. disabled= {!this.showingButtons}
  97. className= 'Toolbar__ManageUsageRights ui-button btn-rights'
  98. onClick= {this.openUsageRightsDialog}
  99. title= {I18n.t('Manage Usage Rights')}
  100. aria-label= {I18n.t('Manage Usage Rights')}
  101. data-tooltip=""
  102. >
  103. <i className= 'icon-files-copyright' />
  104. </button>
  105. );
  106. }
  107. }
  108. Toolbar.renderCopyCourseButton = function (canManage) {
  109. if (canManage) {
  110. return (
  111. <button
  112. type='button'
  113. disabled= {!this.showingButtons}
  114. className= 'ui-button btn-move'
  115. onClick= {function(event) {
  116. openMoveDialog(this.props.selectedItems, {
  117. contextType: this.props.contextType,
  118. contextId: this.props.contextId,
  119. returnFocusTo: event.target,
  120. clearSelectedItems: this.props.clearSelectedItems,
  121. onMove: this.props.onMove
  122. })
  123. }.bind(this)}
  124. title= {I18n.t('Move')}
  125. aria-label= {I18n.t('Move')}
  126. data-tooltip=""
  127. >
  128. <i className='icon-updown' />
  129. </button>
  130. );
  131. }
  132. }
  133. Toolbar.renderDownloadButton = function () {
  134. if (this.getItemsToDownload().length) {
  135. if ((this.props.selectedItems.length === 1) && this.props.selectedItems[0].get('url')) {
  136. return (
  137. <a
  138. className= 'ui-button btn-download'
  139. href= {this.props.selectedItems[0].get('url')}
  140. download= {true}
  141. title= {this.downloadTitle}
  142. aria-label= {this.downloadTitle}
  143. data-tooltip=""
  144. >
  145. <i className='icon-download' />
  146. </a>
  147. );
  148. } else {
  149. return (
  150. <button
  151. type= 'button'
  152. disabled= {!this.showingButtons}
  153. className='ui-button btn-download'
  154. onClick= {this.downloadSelectedAsZip}
  155. title= {this.downloadTitle}
  156. aria-label= {this.downloadTitle}
  157. data-tooltip=""
  158. >
  159. <i className='icon-download'/>
  160. </button>
  161. );
  162. }
  163. }
  164. }
  165. Toolbar.componentDidUpdate = function (prevProps) {
  166. if (prevProps.selectedItems.length !== this.props.selectedItems.length){
  167. $.screenReaderFlashMessageExclusive(I18n.t({one: '%{count} item selected', other: '%{count} items selected'}, {count: this.props.selectedItems.length}))
  168. }
  169. }
  170. Toolbar.renderRestrictedAccessButtons = function (canManage) {
  171. if (canManage){
  172. return (
  173. <button
  174. type= 'button'
  175. disabled= {!this.showingButtons}
  176. className= 'ui-button btn-restrict'
  177. onClick= {this.openRestrictedDialog}
  178. title= {I18n.t('Manage Access')}
  179. aria-label= {I18n.t('Manage Access')}
  180. data-tooltip=""
  181. >
  182. <i className= 'icon-cloud-lock' />
  183. </button>
  184. );
  185. }
  186. }
  187. Toolbar.render = function () {
  188. var selectedItemIsFolder = this.props.selectedItems.every(function(item) {
  189. return item instanceof Folder;
  190. });
  191. var submissionsFolderSelected = this.props.currentFolder && this.props.currentFolder.get('for_submissions');
  192. submissionsFolderSelected = submissionsFolderSelected || this.props.selectedItems.some(function(item) {
  193. return item.get('for_submissions');
  194. });
  195. var restrictedByMasterCourse = this.props.selectedItems.some(function(item) {
  196. return item.get('restricted_by_master_course') && item.get('is_master_course_child_content');
  197. });
  198. var canManage = this.props.userCanManageFilesForContext && !submissionsFolderSelected && !restrictedByMasterCourse;
  199. this.showingButtons = this.props.selectedItems.length
  200. if (this.showingButtons === 1) {
  201. this.downloadTitle = I18n.t('Download');
  202. }
  203. var formClassName = classnames({
  204. "ic-Input-group" : true,
  205. "ef-search-form" : true,
  206. "ef-search-form--showing-buttons" : this.showingButtons
  207. });
  208. var buttonSetClasses = classnames({
  209. "ui-buttonset" : true,
  210. "screenreader-only" : !this.showingButtons
  211. });
  212. var viewBtnClasses = classnames({
  213. 'ui-button': true,
  214. 'btn-view': true,
  215. 'Toolbar__ViewBtn--onlyfolders': selectedItemIsFolder
  216. });
  217. return (
  218. <header
  219. className='ef-header'
  220. role='region'
  221. aria-label= {I18n.t('Files Toolbar')}
  222. >
  223. <form
  224. className= { formClassName }
  225. onSubmit={this.onSubmitSearch}
  226. >
  227. <input
  228. placeholder= {I18n.t('Search for files')}
  229. aria-label= {I18n.t('Search for files')}
  230. type= 'search'
  231. ref='searchTerm'
  232. className='ic-Input'
  233. defaultValue= {this.props.query.search_term}
  234. />
  235. <button
  236. className='Button'
  237. type='submit'
  238. >
  239. <i className='icon-search' />
  240. <span className='screenreader-only'>
  241. {I18n.t('Search for files') }
  242. </span>
  243. </button>
  244. </form>
  245. <div className='ef-header__secondary'>
  246. <div className={buttonSetClasses}>
  247. <a
  248. ref= 'previewLink'
  249. href= '#'
  250. onClick= {!selectedItemIsFolder && preventDefault(this.openPreview)}
  251. className= {viewBtnClasses}
  252. title= {selectedItemIsFolder ? I18n.t('Viewing folders is not available') : I18n.t('View')}
  253. role= 'button'
  254. aria-label= {selectedItemIsFolder ? I18n.t('Viewing folders is not available') : I18n.t('View')}
  255. data-tooltip=""
  256. disabled= {!this.showingButtons || selectedItemIsFolder}
  257. tabIndex= {selectedItemIsFolder ? -1 : 0}
  258. >
  259. <i className= 'icon-eye' />
  260. </a>
  261. { this.renderRestrictedAccessButtons(canManage && this.props.userCanRestrictFilesForContext) }
  262. { this.renderDownloadButton() }
  263. { this.renderCopyCourseButton(canManage) }
  264. { this.renderManageUsageRightsButton(canManage) }
  265. { this.renderDeleteButton(canManage) }
  266. </div>
  267. <span className= 'ef-selected-count hidden-tablet hidden-phone'>
  268. {I18n.t({one: '%{count} item selected', other: '%{count} items selected'}, {count: this.props.selectedItems.length})}
  269. </span>
  270. { this.renderUploadAddFolderButtons(canManage) }
  271. </div>
  272. </header>
  273. );
  274. }
  275. export default React.createClass(Toolbar)