listdir_components.mako 101 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552
  1. ## Licensed to Cloudera, Inc. under one
  2. ## or more contributor license agreements. See the NOTICE file
  3. ## distributed with this work for additional information
  4. ## regarding copyright ownership. Cloudera, Inc. licenses this file
  5. ## to you under the Apache License, Version 2.0 (the
  6. ## "License"); you may not use this file except in compliance
  7. ## with the License. You may obtain a copy of the License at
  8. ##
  9. ## http://www.apache.org/licenses/LICENSE-2.0
  10. ##
  11. ## Unless required by applicable law or agreed to in writing, software
  12. ## distributed under the License is distributed on an "AS IS" BASIS,
  13. ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. ## See the License for the specific language governing permissions and
  15. ## limitations under the License.
  16. <%!
  17. import datetime
  18. from django.template.defaultfilters import urlencode, stringformat, filesizeformat, date, time, escape
  19. from desktop.lib.django_util import reverse_with_get, extract_field_data
  20. from django.utils.encoding import smart_str
  21. from django.utils.translation import ugettext as _
  22. from filebrowser.conf import ENABLE_EXTRACT_UPLOADED_ARCHIVE
  23. %>
  24. <%namespace name="edit" file="editor_components.mako" />
  25. <%def name="list_table_chooser(files, path, current_request_path)">
  26. ${_table(files, path, current_request_path, 'chooser')}
  27. </%def>
  28. <%def name="list_table_browser(files, path, current_request_path, show_download_button, cwd_set=True)">
  29. ${_table(files, path, current_request_path, 'view', show_download_button)}
  30. </%def>
  31. <%def name="_table(files, path, current_request_path, view, show_download_button)">
  32. <link href="${ static('filebrowser/css/listdir_components.css') }" rel="stylesheet" type="text/css">
  33. <div data-bind="visible: isLoading">
  34. <i class="fa fa-spinner fa-spin hue-spinner-large hue-spinner-center muted"></i>
  35. </div>
  36. <table id="fileBrowserTable" class="table table-condensed table-huedatatable tablescroller-disable" data-bind="style: {'opacity': isLoading() ? '.5': '1'}">
  37. <thead>
  38. <tr>
  39. <th class="sorting_disabled" width="1%"><div data-bind="click: selectAll, css: { 'hue-checkbox': true, 'fa': true, 'fa-check': allSelected}" class="select-all"></div></th>
  40. <th class="sortable sorting" data-sort="type" width="1%" data-bind="click: sort">&nbsp;</th>
  41. <th class="sortable sorting_asc" data-sort="name" data-bind="click: sort">${_('Name')}</th>
  42. <th class="sortable sorting" data-sort="size" width="10%" data-bind="click: sort">${_('Size')}</th>
  43. <th class="sortable sorting" data-sort="user" width="10%" data-bind="click: sort">${_('User')}</th>
  44. <th class="sortable sorting" data-sort="group" width="10%" data-bind="click: sort">${_('Group')}</th>
  45. <th class="sorting_disabled" width="10%">${_('Permissions')}</th>
  46. <th class="sortable sorting" data-sort="mtime" width="15%" data-bind="click: sort">${_('Date')}</th>
  47. </tr>
  48. </thead>
  49. <tbody id="files" data-bind="template: {name: 'fileTemplate', foreach: files}"></tbody>
  50. <tfoot>
  51. <tr data-bind="visible: files().length === 0 && !isLoading()">
  52. <td colspan="8">
  53. <div class="alert">
  54. ${_('There are no files matching the search criteria.')}
  55. </div>
  56. </td>
  57. </tr>
  58. </tfoot>
  59. </table>
  60. <div class="pagination">
  61. <div class="pull-right">
  62. <div class="form-inline pagination-input-form inline">
  63. <span>${_('Page')}</span>
  64. <input type="text" data-bind="value: page().number, valueUpdate: 'afterkeydown', event: { change: skipTo }" class="pagination-input" />
  65. <input type="hidden" id="current_page" data-bind="value: page().number" />
  66. ${_('of')} <span data-bind="text: page().num_pages"></span>
  67. </div>
  68. <ul class="inline">
  69. <li class="first-page prev" data-bind="css: { 'disabled': (page().number === page().start_index || page().num_pages <= 1) }">
  70. <a href="javascript:void(0);" data-bind="click: firstPage" title="${_('First page')}"><i class="fa fa-fast-backward"></i></a>
  71. </li>
  72. <li class="previous-page" data-bind="css: { 'disabled': (page().number === page().start_index || page().num_pages <= 1) }">
  73. <a href="javascript:void(0);" data-bind="click: previousPage" title="${_('Previous page')}"><i class="fa fa-backward"></i></a>
  74. </li>
  75. <li class="next-page" data-bind="css: { 'disabled': page().number === page().num_pages }">
  76. <a href="javascript:void(0);" data-bind="click: nextPage" title="${_('Next page')}"><i class="fa fa-forward"></i></a>
  77. </li>
  78. <li class="last-page next" data-bind="css: { 'disabled': page().number === page().num_pages }">
  79. <a href="javascript:void(0);" data-bind="click: lastPage" title="${_('Last page')}"><i class="fa fa-fast-forward"></i></a>
  80. </li>
  81. </ul>
  82. </div>
  83. <p>${_('Show')}
  84. <select class="input-mini" data-bind="options: recordsPerPageChoices, value: recordsPerPage"></select>
  85. ${_('of')} <span data-bind="text: page().total_count"></span> ${_('items')}
  86. </p>
  87. </div>
  88. <!-- extract modal -->
  89. <div id="confirmExtractModal" class="modal hide fade">
  90. <div class="modal-header">
  91. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  92. <h2 class="modal-title">${ _('Extract Archive') }</h2>
  93. </div>
  94. <!-- ko if: selectedFile -->
  95. <div class="modal-body">
  96. <p>${_('Start a task to extract the contents of this archive.')}</p>
  97. <ul>
  98. <li>
  99. <span data-bind="text: selectedFile().name"> </span>
  100. </li>
  101. </ul>
  102. </div>
  103. <div class="modal-footer">
  104. <a class="btn" data-dismiss="modal">${_('No')}</a>
  105. <input type="submit" value="${_('Yes')}" class="btn btn-primary" data-bind="click: extractSelectedArchive"/>
  106. </div>
  107. <!-- /ko -->
  108. </div>
  109. <!-- compress modal -->
  110. <div id="confirmCompressModal" class="modal hide fade">
  111. <div class="modal-header">
  112. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  113. <h2 class="modal-title">${ _('Compress Files') }</h2>
  114. </div>
  115. <!-- ko if: selectedFiles -->
  116. <div class="modal-body">
  117. <div class="form-inline">
  118. <label>${_('Archive Name')} <input data-bind="textInput: compressArchiveName" type="text" class="margin-left-5 input-xlarge"></label>
  119. <p class="margin-top-20">${_('Start a task to compress the selected file(s) and/or folder(s)')}</p>
  120. <ul data-bind="foreach: selectedFiles()">
  121. <li>
  122. <i data-bind="css: { 'fa fa-fw muted': true, 'fa-file-o': type == 'file', 'fa-folder-o': type != 'file' }"></i>
  123. <span data-bind="text: $data.name"> </span>
  124. </li>
  125. </ul>
  126. </div>
  127. </div>
  128. <div class="modal-footer">
  129. <a class="btn" data-dismiss="modal">${_('Cancel')}</a>
  130. <input type="submit" value="${_('Compress')}" class="btn btn-primary" data-bind="click: archiveOverrideWarning, enable: compressArchiveName().length > 0"/>
  131. </div>
  132. <!-- /ko -->
  133. </div>
  134. <!-- compress warning modal -->
  135. <div id="compressWarningModal" class="modal hide fade">
  136. <div class="modal-header">
  137. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  138. <h2 class="modal-title">${ _('Are you sure you want to override the existing archive?') }</h2>
  139. </div>
  140. <!-- ko if: compressArchiveName -->
  141. <div class="modal-body">
  142. <ul>
  143. <li>
  144. <span data-bind="text: compressArchiveName"> </span>
  145. </li>
  146. </ul>
  147. </div>
  148. <div class="modal-footer">
  149. <a class="btn" data-dismiss="modal" data-bind="click: confirmCompressFiles">${_('No')}</a>
  150. <input type="submit" value="${_('Yes')}" class="btn btn-danger" data-bind="click: compressSelectedFiles"/>
  151. </div>
  152. <!-- /ko -->
  153. </div>
  154. <!-- delete modal -->
  155. <div id="deleteModal" class="modal hide fade">
  156. <div class="modal-header">
  157. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  158. <h2 class="modal-title">${ _('Confirm Delete') }</h2>
  159. </div>
  160. <div class="modal-body">
  161. <!-- ko if: isS3() && isS3Root() -->
  162. <p>${_('Are you sure you want to delete these buckets?')}</p>
  163. <p class="muted">${_('Deleting a bucket will delete all of its contents and release the bucket name to be reserved by others.')}</p>
  164. <!-- /ko -->
  165. <!-- ko ifnot: isS3() && isS3Root() -->
  166. <!-- ko ifnot: $root.skipTrash -->
  167. <p>${_('Are you sure you want to delete these files?')}</p>
  168. <!-- /ko -->
  169. <!-- ko if: $root.skipTrash -->
  170. <p>${_('Are you sure you want to permanently delete these files?')}</p>
  171. <!-- /ko -->
  172. <ul data-bind="foreach: $root.selectedFiles">
  173. <li data-bind="visible: $index() <= 10">
  174. <span data-bind="text: name"></span>
  175. </li>
  176. </ul>
  177. <!-- ko if: $root.selectedFiles().length > 10 -->
  178. ${_('and')} <span data-bind="text: $root.selectedFiles().length - 10"></span> ${_('others')}.
  179. <!-- /ko -->
  180. <!-- /ko -->
  181. </div>
  182. <div class="modal-footer">
  183. <form id="deleteForm" action="/filebrowser/rmtree" method="POST" enctype="multipart/form-data" class="form-stacked">
  184. ${ csrf_token(request) | n,unicode }
  185. <a class="btn" data-dismiss="modal">${_('No')}</a>
  186. <input type="submit" value="${_('Yes')}" class="btn btn-danger" />
  187. </form>
  188. </div>
  189. </div>
  190. <!-- restore modal -->
  191. <div id="restoreTrashModal" class="modal hide fade">
  192. <div class="modal-header">
  193. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  194. <h2 class="modal-title">${ _('Confirm Restore') }</h2>
  195. </div>
  196. <div class="modal-body">
  197. <p>${_('Are you sure you want to restore these files?')}</p>
  198. </div>
  199. <div class="modal-footer">
  200. <form id="restoreTrashForm" action="/filebrowser/trash/restore" method="POST" enctype="multipart/form-data" class="form-stacked">
  201. ${ csrf_token(request) | n,unicode }
  202. <a class="btn" data-dismiss="modal">${_('No')}</a>
  203. <input type="submit" value="${_('Yes')}" class="btn btn-primary" />
  204. </form>
  205. </div>
  206. </div>
  207. <!-- purge modal -->
  208. <div id="purgeTrashModal" class="modal hide fade">
  209. <div class="modal-header">
  210. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  211. <h2 class="modal-title">${ _('Confirm empty trash') }</h2>
  212. </div>
  213. <div class="modal-body">
  214. <p>${_('Are you sure you want to permanently delete all your trash?')}</p>
  215. </div>
  216. <div class="modal-footer">
  217. <form id="purgeTrashForm" action="/filebrowser/trash/purge" method="POST" enctype="multipart/form-data" class="form-stacked">
  218. ${ csrf_token(request) | n,unicode }
  219. <a class="btn" data-dismiss="modal">${_('Cancel')}</a>
  220. <input type="submit" value="${_('Delete all')}" class="btn btn-danger" />
  221. </form>
  222. </div>
  223. </div>
  224. <!-- rename modal -->
  225. <form id="renameForm" action="/filebrowser/rename?next=${current_request_path | n,unicode }" method="POST" enctype="multipart/form-data" class="form-inline form-padding-fix">
  226. ${ csrf_token(request) | n,unicode }
  227. <div id="renameModal" class="modal hide fade">
  228. <div class="modal-header">
  229. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  230. <h2 class="modal-title">${_('Renaming:')} <span id="renameFileName">file name</span></h2>
  231. </div>
  232. <div class="modal-body">
  233. <label>${_('New name')} <input id="newNameInput" name="dest_path" value="" type="text" class="input-xlarge"/></label>
  234. </div>
  235. <div class="modal-footer">
  236. <div id="renameNameRequiredAlert" class="hide" style="position: absolute; left: 10px;">
  237. <span class="label label-important">${_('Name is required.')}</span>
  238. </div>
  239. <div id="renameNameExistsAlert" class="hide" style="position: absolute; left: 10px;">
  240. <span class="label label-important"><span class="newName"></span> ${_('already exists.')}</span>
  241. </div>
  242. <input id="renameSrcPath" type="hidden" name="src_path">
  243. <a class="btn" data-dismiss="modal">${_('Cancel')}</a>
  244. <input type="submit" value="${_('Rename')}" class="btn btn-primary" />
  245. </div>
  246. </div>
  247. </form>
  248. <!-- set replication factor modal -->
  249. <form id="setReplicationFactorForm" action="/filebrowser/set_replication?next=${current_request_path | n,unicode }" method="POST" enctype="multipart/form-data" class="form-inline form-padding-fix">
  250. ${ csrf_token(request) | n,unicode }
  251. <div id="setReplicationModal" class="modal hide fade">
  252. <div class="modal-header">
  253. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  254. <h2 class="modal-title">${_('Setting replication factor for :')} <span id="setReplFileName">file name</span></h2>
  255. </div>
  256. <div class="modal-body">
  257. <label>${_('Replication factor')} <input id="newRepFactorInput" name="replication_factor" value="" type="number" class="input-mini"/></label>
  258. </div>
  259. <div class="modal-footer">
  260. <div id="replicationFactorRequiredAlert" class="hide" style="position: absolute; left: 10px;">
  261. <span class="label label-important">${_('Replication factor is required.')}</span>
  262. </div>
  263. <input id="SrcPath" type="hidden" name="src_path">
  264. <a class="btn" data-dismiss="modal">${_('Cancel')}</a>
  265. <input type="submit" value="${_('Submit')}" class="btn btn-primary" />
  266. </div>
  267. </div>
  268. </form>
  269. <!-- chown modal -->
  270. % if is_fs_superuser:
  271. <form id="chownForm" action="/filebrowser/chown" method="POST" enctype="multipart/form-data" class="form-stacked form-padding-fix">
  272. ${ csrf_token(request) | n,unicode }
  273. <div id="changeOwnerModal" class="modal hide fade">
  274. <%
  275. select_filter = is_fs_superuser and 'SelectWithOther' or ''
  276. %>
  277. <div class="modal-header">
  278. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  279. <h2 class="modal-title">${_('Change Owner/Group')}</h2>
  280. </div>
  281. <div class="modal-body change-owner-modal-body clearfix" >
  282. <div class="alert alert-message block-message info">${_('Note: Only the Hadoop superuser, "%(superuser)s" or the HDFS supergroup, "%(supergroup)s" on this file system, may change the owner of a file.') % dict(superuser=superuser, supergroup=supergroup)}</div>
  283. <div style="padding-left: 15px; padding-bottom: 10px;">
  284. <label>${_('User')}</label>
  285. ${ edit.selection("user", users, user.username, "user_other") }
  286. <label>${_('Group')}</label>
  287. ${ edit.selection("group", groups, 'supergroup', "group_other") }
  288. <br />
  289. <label style="display: inline;">${_('Recursive')}</label> <input type="checkbox" name="recursive" style="margin-bottom:4px">
  290. </div>
  291. </div>
  292. <div class="modal-footer" style="padding-top: 10px;">
  293. <div id="chownRequired" class="hide" style="position: absolute; left: 10px;">
  294. <span class="label label-important">${_('Name is required.')}</span>
  295. </div>
  296. <a class="btn" onclick="$('#changeOwnerModal').modal('hide');">${_('Cancel')}</a>
  297. <input class="btn btn-primary" type="submit" value="${_('Submit')}" />
  298. </div>
  299. </div>
  300. </form>
  301. % endif
  302. <!-- chmod modal -->
  303. <form action="/filebrowser/chmod" method="POST" enctype="multipart/form-data" class="form-inline form-padding-fix" id="chmodForm">
  304. ${ csrf_token(request) | n,unicode }
  305. <div id="changePermissionModal" class="modal hide fade">
  306. <div class="modal-header">
  307. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  308. <h2 class="modal-title">${_('Change Permissions')}</h2>
  309. </div>
  310. <div class="modal-body table-margin">
  311. <table class="table table-condensed">
  312. <thead>
  313. <tr>
  314. <th>&nbsp;</th>
  315. <th class="center">${_('User')}</th>
  316. <th class="center">${_('Group')}</th>
  317. <th class="center">${_('Other')}</th>
  318. <th class="center">&nbsp;</th>
  319. <th width="120">&nbsp;</th>
  320. </tr>
  321. </thead>
  322. <tbody>
  323. <tr>
  324. <td><strong>${_('Read')}</strong></td>
  325. <td class="center"><input type="checkbox" data-bind="attr: {checked: selectedFile.mode }" checked="" name="user_read"></td>
  326. <td class="center"><input type="checkbox" data-bind="attr: {checked: selectedFile.mode }" checked="" name="group_read"></td>
  327. <td class="center"><input type="checkbox" data-bind="attr: {checked: selectedFile.mode }" checked="" name="other_read"></td>
  328. <td colspan="2">&nbsp;</td>
  329. </tr>
  330. <tr>
  331. <td><strong>${_('Write')}</strong></td>
  332. <td class="center"><input type="checkbox" data-bind="attr: {checked: selectedFile.mode }" checked="" name="user_write"></td>
  333. <td class="center"><input type="checkbox" data-bind="attr: {checked: selectedFile.mode }" checked="" name="group_write"></td>
  334. <td class="center"><input type="checkbox" data-bind="attr: {checked: selectedFile.mode }" checked="" name="other_write"></td>
  335. <td colspan="2">&nbsp;</td>
  336. </tr>
  337. <tr>
  338. <td><strong>${_('Execute')}</strong></td>
  339. <td class="center"><input type="checkbox" checked="" name="user_execute"></td>
  340. <td class="center"><input type="checkbox" checked="" name="group_execute"></td>
  341. <td class="center"><input type="checkbox" checked="" name="other_execute"></td>
  342. <td colspan="2">&nbsp;</td>
  343. </tr>
  344. <tr>
  345. <td><strong>${_('Sticky')}</strong></td>
  346. <td colspan="3">&nbsp;</td>
  347. <td class="center"><input type="checkbox" name="sticky"></td>
  348. <td>&nbsp;</td>
  349. </tr>
  350. <tr>
  351. <td><strong>${_('Recursive')}</strong></td>
  352. <td colspan="3">&nbsp;</td>
  353. <td class="center"><input type="checkbox" name="recursive"></td>
  354. <td>&nbsp;</td>
  355. </tr>
  356. </tbody>
  357. </table>
  358. </div>
  359. <div class="modal-footer" style="padding-top: 10px;">
  360. <a class="btn" onclick="$('#changePermissionModal').modal('hide');">${_('Cancel')}</a>
  361. <input class="btn btn-primary" type="submit" value="${_('Submit')}"/>
  362. </div>
  363. </div>
  364. </form>
  365. <!-- move modal -->
  366. <form id="moveForm" action="/filebrowser/move" method="POST" enctype="multipart/form-data" class="form-inline form-padding-fix">
  367. ${ csrf_token(request) | n,unicode }
  368. <div id="moveModal" class="modal hide fade">
  369. <div class="modal-header">
  370. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  371. <h2 class="modal-title">${_('Move to')}</h2>
  372. </div>
  373. <div class="modal-body" style="max-height: 400px; height: 340px; overflow-x:hidden">
  374. <div id="moveFilechooser"></div>
  375. </div>
  376. <div class="modal-footer">
  377. <div>
  378. <input type="text" class="input-xlarge disable-autofocus" value="" name="dest_path" id="moveDestination" placeholder="${_('Select a folder or paste a path...')}" />
  379. <span id="moveNameRequiredAlert" class="hide label label-important">${_('Required')}</span>
  380. </div>
  381. <a class="btn" onclick="$('#moveModal').modal('hide');">${_('Cancel')}</a>
  382. <input class="btn btn-primary" type="submit" value="${_('Move')}"/>
  383. </div>
  384. </div>
  385. </form>
  386. <!-- copy modal -->
  387. <form id="copyForm" action="/filebrowser/copy" method="POST" enctype="multipart/form-data" class="form-inline form-padding-fix">
  388. ${ csrf_token(request) | n,unicode }
  389. <div id="copyModal" class="modal hide fade">
  390. <div class="modal-header">
  391. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  392. <h2 class="modal-title">${_('Copy to')}</h2>
  393. </div>
  394. <div class="modal-body" style="max-height: 400px; height: 340px; overflow-x:hidden">
  395. <div id="copyFilechooser"></div>
  396. </div>
  397. <div class="modal-footer">
  398. <div>
  399. <input type="text" class="input-xlarge disable-autofocus" value="" name="dest_path" id="copyDestination" placeholder="${_('Select a folder or paste a path...')}" />
  400. <span id="copyNameRequiredAlert" class="hide label label-important">${_('Required')}</span>
  401. </div>
  402. <a class="btn" onclick="$('#copyModal').modal('hide');">${_('Cancel')}</a>
  403. <input class="btn btn-primary" type="submit" value="${_('Copy')}"/>
  404. </div>
  405. </div>
  406. </form>
  407. <!-- upload file modal -->
  408. <div id="uploadFileModal" class="modal hide fade">
  409. <div class="modal-header">
  410. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  411. <h2 class="modal-title">${_('Upload to')} <span data-bind="text: currentPath"></span></h2>
  412. </div>
  413. <div class="modal-body form-inline">
  414. <div id="fileUploader" class="uploader">
  415. <noscript>
  416. <p>${_('Enable JavaScript to use the file uploader.')}</p>
  417. </noscript>
  418. </div>
  419. </div>
  420. <div class="modal-footer"></div>
  421. </div>
  422. <!-- new directory modal -->
  423. <form id="createDirectoryForm" data-bind="submit: createDirectory" method="POST" enctype="multipart/form-data" class="form-inline form-padding-fix">
  424. ${ csrf_token(request) | n,unicode }
  425. <div id="createDirectoryModal" class="modal hide fade">
  426. <div class="modal-header">
  427. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  428. <!-- ko if: (!isS3() && !isABFS()) || (isS3() && !isS3Root()) || (isABFS() && !isABFSRoot()) -->
  429. <h2 class="modal-title">${_('Create Directory')}</h2>
  430. <!-- /ko -->
  431. <!-- ko if: isS3() && isS3Root() -->
  432. <h2 class="modal-title">${_('Create Bucket')}</h2>
  433. <!-- /ko -->
  434. <!-- ko if: isABFS() && isABFSRoot() -->
  435. <h2 class="modal-title">${_('Create File System')}</h2>
  436. <!-- /ko -->
  437. </div>
  438. <div class="modal-body">
  439. <label>
  440. <!-- ko if: (!isS3() && !isABFS()) || (isS3() && !isS3Root()) || (isABFS() && !isABFSRoot()) -->
  441. ${_('Directory Name')}
  442. <!-- /ko -->
  443. <!-- ko if: isS3() && isS3Root() -->
  444. ${_('Bucket Name')}
  445. <!-- /ko -->
  446. <!-- ko if: isABFS() && isABFSRoot() -->
  447. ${_('File System Name')}
  448. <!-- /ko -->
  449. <input id="newDirectoryNameInput" name="name" value="" type="text" class="input-xlarge"/></label>
  450. <input type="hidden" name="path" data-bind="value: currentPath"/>
  451. </div>
  452. <div class="modal-footer">
  453. <div id="directoryNameRequiredAlert" class="hide" style="position: absolute; left: 10px;">
  454. <span class="label label-important">${_('Directory name is required.')}</span>
  455. </div>
  456. <div id="directoryNameExistsAlert" class="hide" style="position: absolute; left: 10px;">
  457. <span class="label label-important"><span class="newName"></span> ${_('already exists.')}</span>
  458. </div>
  459. <div id="smallFileSystemNameAlert" class="hide" style="position: absolute; left: 10px;">
  460. <span class="label label-important"><span class="newName"></span> ${_('File system requires namesize to be 3 or more characters')}</span>
  461. </div>
  462. <a class="btn" href="#" data-dismiss="modal">${_('Cancel')}</a>
  463. <input class="btn btn-primary" type="submit" value="${_('Create')}" />
  464. </div>
  465. </div>
  466. </form>
  467. <!-- new file modal -->
  468. <form id="createFileForm" data-bind="submit: createFile" method="POST" enctype="multipart/form-data" class="form-inline form-padding-fix">
  469. ${ csrf_token(request) | n,unicode }
  470. <div id="createFileModal" class="modal hide fade">
  471. <div class="modal-header">
  472. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  473. <h2 class="modal-title">${_('Create File')}</h2>
  474. </div>
  475. <div class="modal-body">
  476. <label>${_('File Name')} <input id="newFileNameInput" name="name" value="" type="text" class="input-xlarge"/></label>
  477. <input type="hidden" name="path" data-bind="value: currentPath"/>
  478. </div>
  479. <div class="modal-footer">
  480. <div id="fileNameRequiredAlert" class="alert-message error hide" style="position: absolute; left: 10px;">
  481. <span class="label label-important">${_('File name is required.')}</span>
  482. </div>
  483. <div id="fileNameExistsAlert" class="hide" style="position: absolute; left: 10px;">
  484. <span class="label label-important"><span class="newName"></span> ${_('already exists.')}</span>
  485. </div>
  486. <a class="btn" href="#" data-dismiss="modal">${_('Cancel')}</a>
  487. <input class="btn btn-primary" type="submit" value="${_('Create')}" />
  488. </div>
  489. </div>
  490. </form>
  491. <!-- content summary modal -->
  492. <div id="contentSummaryModal" class="modal hide fade">
  493. <div class="modal-header">
  494. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  495. <!-- ko if: selectedFile -->
  496. <h2 class="modal-title" style="word-break: break-all">${_('Summary for')} <span data-bind="text: selectedFile().path"></span></h2>
  497. <!--/ko -->
  498. </div>
  499. <div class="modal-body">
  500. <div data-bind="visible: isLoadingSummary"><i class="fa fa-spin fa-spinner fa-2x fa-fw" style="color: #CCC"></i></div>
  501. <table class="table table-condensed" data-bind="visible: !isLoadingSummary()">
  502. <tr>
  503. <th>${ _('Disk space consumed') }</th>
  504. <td data-bind="text: formatBytes(contentSummary().spaceConsumed(), 4)"></td>
  505. </tr>
  506. <tr>
  507. <th>${ _('Bytes used') }</th>
  508. <td data-bind="text: formatBytes(contentSummary().length(), 4)"></td>
  509. </tr>
  510. <tr>
  511. <th>${ _('Namespace quota') }</th>
  512. <td data-bind="text: formatBytes(contentSummary().quota(), 4)"></td>
  513. </tr>
  514. <tr>
  515. <th>${ _('Disk space quota') }</th>
  516. <td data-bind="text: formatBytes(contentSummary().spaceQuota(), 4)"></td>
  517. </tr>
  518. <tr>
  519. <th>${ _('Replication factor') }</th>
  520. <td data-bind="text: contentSummary().replication()"></td>
  521. </tr>
  522. <tr>
  523. <th>${ _('Number of directories') }</th>
  524. <td data-bind="text: contentSummary().directoryCount()"></td>
  525. </tr>
  526. <tr>
  527. <th>${ _('Number of files') }</th>
  528. <td data-bind="text: contentSummary().fileCount()"></td>
  529. </tr>
  530. </table>
  531. </div>
  532. <div class="modal-footer">
  533. <a class="btn" data-dismiss="modal">${_('Close')}</a>
  534. </div>
  535. </div>
  536. <!-- actions context menu -->
  537. <ul class="context-menu dropdown-menu">
  538. <!-- ko ifnot: $root.inTrash -->
  539. <li data-bind="visible: (!isS3() && !isABFS()) || (isS3() && !isS3Root()) || (isABFS() && !isABFSRoot()), css: {'disabled': $root.selectedFiles().length != 1 || isCurrentDirSelected().length > 0}">
  540. <a href="javascript: void(0)" title="${_('Rename')}" data-bind="click: ($root.selectedFiles().length == 1 && isCurrentDirSelected().length == 0) ? $root.renameFile: void(0)"><i class="fa fa-fw fa-font"></i>
  541. ${_('Rename')}</a></li>
  542. <li data-bind="visible: (!isS3() && !isABFS()) || (isS3() && !isS3Root()) || (isABFS() && !isABFSRoot()), css: {'disabled': $root.selectedFiles().length == 0 || isCurrentDirSelected().length > 0}">
  543. <a href="javascript: void(0)" title="${_('Move')}" data-bind="click: ( $root.selectedFiles().length > 0 && isCurrentDirSelected().length == 0) ? $root.move: void(0)"><i class="fa fa-fw fa-random"></i> ${_('Move')}</a></li>
  544. <li data-bind="visible: (!isS3() && !isABFS()) || (isS3() && !isS3Root()) || (isABFS() && !isABFSRoot()), css: {'disabled': $root.selectedFiles().length == 0 || isCurrentDirSelected().length > 0}">
  545. <a href="javascript: void(0)" title="${_('Copy')}" data-bind="click: ($root.selectedFiles().length > 0 && isCurrentDirSelected().length == 0) ? $root.copy: void(0)"><i class="fa fa-fw fa-files-o"></i> ${_('Copy')}</a></li>
  546. % if show_download_button:
  547. <li data-bind="css: {'disabled': $root.inTrash() || $root.selectedFiles().length != 1 || selectedFile().type != 'file'}">
  548. <a href="javascript: void(0)" title="${_('Download')}" data-bind="click: (!$root.inTrash() && $root.selectedFiles().length == 1 && selectedFile().type == 'file') ? $root.downloadFile: void(0)">
  549. <i class="fa fa-fw fa-arrow-circle-o-down"></i> ${_('Download')}</a></li>
  550. % endif
  551. <li class="divider" data-bind="visible: isPermissionEnabled()"></li>
  552. % if is_fs_superuser:
  553. <li data-bind="css: {'disabled': $root.isCurrentDirSentryManaged || selectedSentryFiles().length > 0 }, visible: isPermissionEnabled()">
  554. <a href="javascript: void(0)" data-bind="visible: !$root.inTrash(), click: $root.changeOwner, enable: $root.selectedFiles().length > 0">
  555. <i class="fa fa-fw fa-user"></i> ${_('Change owner / group')}
  556. </a>
  557. </li>
  558. % endif
  559. <li data-bind="css: {'disabled': $root.isCurrentDirSentryManaged() || selectedSentryFiles().length > 0 }, visible: isPermissionEnabled()">
  560. <a href="javascript: void(0)" data-bind="visible: !$root.inTrash(), click: $root.changePermissions, enable: $root.selectedFiles().length > 0">
  561. <i class="fa fa-fw fa-list-alt"></i> ${_('Change permissions')}
  562. </a>
  563. </li>
  564. <li class="divider" data-bind="visible: isCompressEnabled() || isReplicationEnabled() || isSummaryEnabled()"></li>
  565. % if is_trash_enabled:
  566. <li data-bind="css: {'disabled': $root.selectedFiles().length == 0 || isCurrentDirSelected().length > 0}">
  567. <a href="javascript: void(0)" data-bind="click: ($root.selectedFiles().length > 0 && isCurrentDirSelected().length == 0) ? $root.trashSelected: void(0)">
  568. <i class="fa fa-fw fa-times"></i> ${_('Move to trash')}</a></li>
  569. % endif
  570. <li><a href="javascript: void(0)" class="delete-link" title="${_('Delete forever')}" data-bind="enable: $root.selectedFiles().length > 0 && isCurrentDirSelected().length == 0, click: $root.deleteSelected"><i class="fa fa-fw fa-bolt"></i> ${_('Delete forever')}</a></li>
  571. <li class="divider" data-bind="visible: isSummaryEnabled()"></li>
  572. <li data-bind="css: {'disabled': selectedFiles().length > 1 }, visible: isSummaryEnabled()">
  573. <a class="pointer" data-bind="click: function(){ selectedFiles().length == 1 ? showSummary(): void(0)}"><i class="fa fa-fw fa-pie-chart"></i> ${_('Summary')}</a>
  574. </li>
  575. <li data-bind="css: {'disabled': inTrash() || !isReplicationEnabled() || selectedFiles().length != 1 || selectedFile().type != 'file'}">
  576. <a href="javascript: void(0)" title="${_('Set Replication')}" data-bind="click: (!inTrash() && isReplicationEnabled() && selectedFiles().length == 1 && selectedFile().type == 'file') ? setReplicationFactor: void(0)">
  577. <i class="fa fa-fw fa-hdd-o"></i> ${_('Set replication')}
  578. </a>
  579. </li>
  580. % if ENABLE_EXTRACT_UPLOADED_ARCHIVE.get():
  581. <li data-bind="css: {'disabled': ! showCompressButton}">
  582. <a href="javascript: void(0)" title="${_('Compress selection into a single archive')}" data-bind="click: function() { setCompressArchiveDefault(); confirmCompressFiles();}, visible: showCompressButton">
  583. <i class="fa fa-fw fa-file-archive-o"></i> ${_('Compress')}</a>
  584. </li>
  585. <li data-bind="css: {'disabled': selectedFiles().length != 1 || !isArchive(selectedFile().name) || !isCompressEnabled()}">
  586. <a href="javascript: void(0)" title="${_('Extract selected archive')}" data-bind="visible: selectedFiles().length == 1 && isArchive(selectedFile().name) && isCompressEnabled(), click: confirmExtractArchive">
  587. <i class="fa fa-fw fa-file-archive-o"></i> ${_('Extract')}</a>
  588. </li>
  589. % endif
  590. <!-- /ko -->
  591. <!-- ko if: $root.inTrash -->
  592. <li><a href="#" title="${_('Restore from trash')}" data-bind="visible: inRestorableTrash() && selectedFiles().length > 0 && isCurrentDirSelected().length == 0, click: restoreTrashSelected"><i class="fa fa-fw fa-cloud-upload"></i> ${_('Restore')}</a></li>
  593. <li class="divider"></li>
  594. <li><a href="#" title="${_('Empty trash')}" data-bind="visible: inTrash(), click: purgeTrash"><i class="fa fa-fw fa-fire"></i> ${_('Empty trash')}</a></li>
  595. <!-- /ko -->
  596. </ul>
  597. <div id="submit-wf-modal" class="modal hide"></div>
  598. <script id="fileTemplate" type="text/html">
  599. <tr class="row-animated" style="cursor: pointer" data-bind="drop: { enabled: name !== '.' && type !== 'file' && (!$root.isS3() || ($root.isS3() && !$root.isS3Root())), value: $data }, event: { mouseover: toggleHover, mouseout: toggleHover, contextmenu: showContextMenu }, click: $root.viewFile, css: { 'row-selected': selected(), 'row-highlighted': highlighted(), 'row-deleted': deleted() }">
  600. <td class="center" data-bind="click: name !== '..' ? handleSelect : void(0)" style="cursor: default">
  601. <div data-bind="multiCheck: '#fileBrowserTable', visible: name != '..', css: { 'hue-checkbox': name != '..', 'fa': name != '..', 'fa-check': selected }"></div>
  602. </td>
  603. <td class="left"><i data-bind="click: $root.viewFile, css: { 'fa': true,
  604. % if 'oozie' in apps:
  605. 'fa-play': $.inArray(name, ['workflow.xml', 'coordinator.xml', 'bundle.xml']) > -1,
  606. % endif
  607. 'fa-file-o': type == 'file', 'fa-folder': type != 'file', 'fa-folder-open': type != 'file' && hovered(), 'fa-cloud': type != 'file' && isBucket() }"></i></td>
  608. <td data-bind="attr: {'title': tooltip}" rel="tooltip">
  609. <!-- ko if: name == '..' -->
  610. <a href="#" data-bind="click: $root.viewFile"><i class="fa fa-level-up"></i></a>
  611. <!-- /ko -->
  612. <!-- ko if: name != '..' -->
  613. <strong><a href="#" class="draggable-fb" data-bind="drag: { enabled: (!$root.isS3() || ($root.isS3() && !$root.isS3Root())), value: $data }, click: $root.viewFile, text: name, attr: { 'draggable': $.inArray(name, ['.', '..', '.Trash']) === -1 && !isBucket()}"></a></strong>
  614. <!-- /ko -->
  615. </td>
  616. <td>
  617. <span data-bind="visible: type == 'file', text: stats.size"></span>
  618. </td>
  619. <td class="truncate-text" style="max-width: 150px">
  620. % if is_fs_superuser:
  621. <span data-bind="text: stats.user, visible: !selected() || $root.isCurrentDirSentryManaged() || isSentryManaged, attr: { 'title': stats.user }"></span>
  622. <a href="#" rel="tooltip" title="${_('Change owner')}" data-original-title="${_('Change owner')}"
  623. data-bind="text: stats.user, visible: !$root.inTrash() && selected() && !$root.isCurrentDirSentryManaged() && !isSentryManaged, click: $root.changeOwner, enable: $root.selectedFiles().length > 0, attr: { 'title': stats.user }"></a>
  624. % else:
  625. <span data-bind="text: stats.user, attr: { 'title': stats.user }"></span>
  626. % endif
  627. </td>
  628. <td class="truncate-text" style="max-width: 150px">
  629. % if is_fs_superuser:
  630. <span data-bind="text: stats.group, visible: ! selected() || $root.isCurrentDirSentryManaged() || isSentryManaged, attr: { 'title': stats.group }"></span>
  631. <a href="#" rel="tooltip" title="${_('Change group')}" data-original-title="${_('Change group')}"
  632. data-bind="text: stats.group, visible: !$root.inTrash() && selected() && !$root.isCurrentDirSentryManaged() && !isSentryManaged, click: $root.changeOwner, attr: { 'title': stats.group }"></a>
  633. % else:
  634. <span data-bind="text: stats.group, attr: { 'title': stats.group }"></span>
  635. % endif
  636. </td>
  637. <td>
  638. <span data-bind="text: permissions, visible: $root.isS3() || !selected() || $root.isCurrentDirSentryManaged() || isSentryManaged"></span>
  639. <a href="#" rel="tooltip" title="${_('Change permissions')}"
  640. data-bind="text: permissions, visible: !$root.isS3() && !$root.inTrash() && selected() && !$root.isCurrentDirSentryManaged() && !isSentryManaged, click: $root.changePermissions" data-original-title="${_('Change permissions')}"></a>
  641. </td>
  642. <td data-bind="text: stats.mtime" style="white-space: nowrap;"></td>
  643. </tr>
  644. </script>
  645. <script src="${ static('desktop/ext/js/datatables-paging-0.1.js') }" type="text/javascript" charset="utf-8"></script>
  646. <script>
  647. var _dragged;
  648. var _dropzone;
  649. ko.bindingHandlers.drag = {
  650. update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
  651. var dragElement = $(element);
  652. try {
  653. dragElement.draggable('destroy');
  654. }
  655. catch (e) {}
  656. var dragOptions = {
  657. helper: 'clone',
  658. revert: true,
  659. revertDuration: 0,
  660. start: function () {
  661. if ($(element).is('[draggable]')) {
  662. viewModel.selected(true);
  663. }
  664. _dragged = ko.utils.unwrapObservable(valueAccessor().value);
  665. },
  666. cursor: "move",
  667. delay: 300
  668. };
  669. dragElement.draggable(dragOptions).disableSelection();
  670. }
  671. };
  672. ko.bindingHandlers.drop = {
  673. update: function (element, valueAccessor) {
  674. var dropElement = $(element);
  675. try {
  676. dropElement.droppable('destroy');
  677. }
  678. catch (e) {}
  679. if (valueAccessor().enabled) {
  680. var dropOptions = {
  681. hoverClass: 'drag-hover',
  682. accept: '.draggable-fb',
  683. drop: function (event, ui) {
  684. var destpath = valueAccessor().value.path;
  685. dropElement.fadeOut(200, function () {
  686. dropElement.fadeIn(200);
  687. });
  688. if (destpath) {
  689. $('#moveDestination').val(destpath);
  690. fileBrowserViewModel.move('nomodal', _dragged);
  691. }
  692. }
  693. };
  694. dropElement.droppable(dropOptions);
  695. }
  696. }
  697. };
  698. var apiHelper = window.apiHelper;
  699. // ajax modal windows
  700. var openChownWindow = function (path, user, group, next) {
  701. $.ajax({
  702. url: "/filebrowser/chown",
  703. data: {"path": path, "user": user, "group": group, "next": next},
  704. beforeSend: function (xhr) {
  705. xhr.setRequestHeader("X-Requested-With", "Hue");
  706. },
  707. dataType: "html",
  708. success: function (data) {
  709. $("#changeOwnerModal").html(data);
  710. $("#changeOwnerModal").modal({
  711. keyboard:true,
  712. show:true
  713. });
  714. },
  715. error: function (xhr, textStatus, errorThrown) {
  716. $(document).trigger("error", xhr.responseText);
  717. resetPrimaryButtonsStatus();
  718. }
  719. });
  720. };
  721. var fileExists = function (newName) {
  722. if (fileBrowserViewModel) {
  723. var files = fileBrowserViewModel.files();
  724. for (var i = 0; i < files.length; i++) {
  725. if (files[i].name == newName) {
  726. return true;
  727. }
  728. }
  729. }
  730. return false;
  731. };
  732. var resetActionbar = function() {
  733. $(".actionbar").attr("style", "min-width: 800px");
  734. $(".actionbar").data("originalWidth", $(".actionbar").width());
  735. $(".actionbarGhost").addClass("hide");
  736. };
  737. var stripHashes = function (str) {
  738. return str.replace(/#/gi, encodeURIComponent("#"));
  739. };
  740. var formatBytes = function (bytes, decimals) {
  741. if (bytes == -1) return "${ _('Not available.') }";
  742. if (bytes == 0) return "0 Byte";
  743. var k = 1024;
  744. var dm = decimals + 1 || 3;
  745. var sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
  746. var i = Math.floor(Math.log(bytes) / Math.log(k));
  747. return (bytes / Math.pow(k, i)).toPrecision(dm) + ' ' + sizes[i];
  748. }
  749. var updateHash = function (hash) {
  750. hash = encodeURI(decodeURIComponent(hash));
  751. %if not is_embeddable:
  752. window.location.hash = hash;
  753. %else:
  754. hueUtils.changeURL('#' + hash);
  755. huePubSub.publish('fb.update.hash');
  756. %endif
  757. }
  758. var Page = function (page) {
  759. if (page != null) {
  760. return {
  761. number: page.number,
  762. num_pages: page.num_pages,
  763. previous_page_number: page.previous_page_number,
  764. next_page_number: page.next_page_number,
  765. start_index: page.start_index,
  766. end_index: page.end_index,
  767. total_count: page.total_count
  768. }
  769. }
  770. return {
  771. }
  772. };
  773. var File = function (file) {
  774. file.tooltip = "";
  775. if (file.name == "."){
  776. file.tooltip = "${_('This folder')}";
  777. }
  778. if (file.name == ".."){
  779. file.tooltip = "${_('One level up')}";
  780. }
  781. return {
  782. name: file.name,
  783. path: file.path,
  784. url: file.url,
  785. type: file.type,
  786. permissions: file.rwx,
  787. mode: file.mode,
  788. isSentryManaged: file.is_sentry_managed,
  789. stats: {
  790. size: file.humansize,
  791. user: file.stats.user,
  792. group: file.stats.group,
  793. mtime: file.mtime,
  794. replication: file.stats.replication
  795. },
  796. isBucket: ko.pureComputed(function(){
  797. return file.path.toLowerCase().indexOf('s3a://') == 0 && file.path.substr(6).indexOf('/') == -1
  798. }),
  799. selected: ko.observable(file.highlighted && fileBrowserViewModel.isArchive(file.name) || false),
  800. highlighted: ko.observable(file.highlighted || false),
  801. deleted: ko.observable(file.deleted || false),
  802. handleSelect: function (row, e) {
  803. e.preventDefault();
  804. e.stopPropagation();
  805. this.selected(!this.selected());
  806. if (!this.selected()) {
  807. hideContextMenu();
  808. }
  809. this.highlighted(false);
  810. this.deleted(false);
  811. fileBrowserViewModel.allSelected(false);
  812. },
  813. // display the context menu when an item is right/context clicked
  814. showContextMenu: function (row, e) {
  815. var cm = $('.context-menu'),
  816. actions = $('#ch-dropdown'),
  817. rect = document.querySelector('body').getBoundingClientRect();
  818. e.stopPropagation();
  819. // close the actions menu from button area if open
  820. if (actions.hasClass('open')) {
  821. actions.removeClass('open');
  822. }
  823. // display context menu and ensure it is on-screen
  824. if ($.inArray(row.name, ['..', '.Trash']) === -1) {
  825. this.selected(true);
  826. var verticalCorrection = 0;
  827. %if is_embeddable:
  828. verticalCorrection = $('.page-content').scrollTop() - $('.navbar-default').height() - $('.banner').height();
  829. %endif
  830. cm.css({ display: 'block', top: e.pageY - 15 + verticalCorrection, left: (e.offsetX < rect.right - 300 ) ? e.offsetX + 100 : e.offsetX - 250 });
  831. } else {
  832. cm.css({ display: 'none' });
  833. }
  834. },
  835. hovered: ko.observable(false),
  836. toggleHover: function (row, e) {
  837. this.hovered(! this.hovered());
  838. },
  839. tooltip:file.tooltip
  840. }
  841. };
  842. var Breadcrumb = function (breadcrumb) {
  843. return {
  844. url: breadcrumb.url,
  845. label: breadcrumb.label,
  846. show: function (breadcrumb, e) {
  847. var isLeftButton = (e.which || e.button) === 1;
  848. if (isLeftButton) {
  849. if (! (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) {
  850. e.stopPropagation();
  851. e.preventDefault();
  852. if (this.url == null || this.url == "") {
  853. // forcing root on empty breadcrumb url
  854. this.url = "/";
  855. }
  856. fileBrowserViewModel.targetPageNum(1);
  857. fileBrowserViewModel.targetPath("${url('filebrowser.views.view', path='')}" + stripHashes(this.url));
  858. updateHash(this.url);
  859. }
  860. else {
  861. window.open($(e.target).attr('href'));
  862. }
  863. }
  864. }
  865. }
  866. };
  867. var FileBrowserModel = function (files, page, breadcrumbs, currentDirPath) {
  868. var self = this;
  869. self.page = ko.observable(new Page(page));
  870. self.recordsPerPageChoices = ["15", "30", "45", "60", "100", "200", "1000"],
  871. self.recordsPerPage = ko.observable(apiHelper.getFromTotalStorage('fb', 'records_per_page', 45));
  872. self.targetPageNum = ko.observable(1);
  873. self.targetPath = ko.observable("${current_request_path | n,unicode }");
  874. self.sortBy = ko.observable("name");
  875. self.sortDescending = ko.observable(false);
  876. self.searchQuery = ko.observable("");
  877. self.skipTrash = ko.observable(false);
  878. self.enableFilterAfterSearch = true;
  879. self.isCurrentDirSentryManaged = ko.observable(false);
  880. self.pendingUploads = ko.observable(0);
  881. self.pendingUploads.subscribe(function (val) {
  882. if (val > 0) {
  883. if ($('#uploadFileModal').data('modal')) {
  884. $('#uploadFileModal').data('modal').$element.off('keyup.dismiss.modal');
  885. if ($('#uploadFileModal').data('modal').$backdrop){
  886. $('#uploadFileModal').data('modal').$backdrop.off('click');
  887. }
  888. }
  889. }
  890. });
  891. self.filesToHighlight = ko.observableArray([]);
  892. self.fileNameSorting = function (l, r) {
  893. if (l.name == "..") {
  894. return -1;
  895. }
  896. else if (r.name == "..") {
  897. return 1;
  898. }
  899. else if (l.name == ".") {
  900. return -1;
  901. }
  902. else if (r.name == ".") {
  903. return 1;
  904. }
  905. else {
  906. var _ret = l.name > r.name ? 1 : -1;
  907. if (self.sortDescending()){
  908. _ret = _ret * -1;
  909. }
  910. return _ret;
  911. }
  912. }
  913. self.files = ko.observableArray(ko.utils.arrayMap(files, function (file) {
  914. return new File(file);
  915. }));
  916. if (self.sortBy() == "name"){
  917. self.files.sort(self.fileNameSorting);
  918. }
  919. self.homeDir = ko.observable("${home_directory}");
  920. self.breadcrumbs = ko.observableArray(ko.utils.arrayMap(breadcrumbs, function (breadcrumb) {
  921. return new Breadcrumb(breadcrumb);
  922. }));
  923. self.sort = function (viewModel, event) {
  924. var el = $(event.currentTarget);
  925. el.siblings(".sortable").attr("class", "sortable sorting");
  926. self.sortBy(el.data("sort"));
  927. el.removeClass("sorting");
  928. if (el.hasClass("sorting_asc")) {
  929. self.sortDescending(true);
  930. } else {
  931. self.sortDescending(false);
  932. }
  933. el.attr("class", "sortable");
  934. if (self.sortDescending() == true) {
  935. el.addClass("sorting_desc");
  936. } else {
  937. el.addClass("sorting_asc");
  938. }
  939. self.retrieveData();
  940. }
  941. self.isLoading = ko.observable(true);
  942. self.allSelected = ko.observable(false);
  943. self.compressArchiveName = ko.observable('');
  944. self.selectedFiles = ko.computed(function () {
  945. return ko.utils.arrayFilter(self.files(), function (file) {
  946. return file.selected();
  947. });
  948. }, self).extend({ rateLimit: { timeout: 500, method: "notifyWhenChangesStop" } });
  949. self.selectedSentryFiles = ko.computed(function () {
  950. return ko.utils.arrayFilter(self.files(), function (file) {
  951. return file.selected() && file.isSentryManaged;
  952. });
  953. }, self).extend({ rateLimit: { timeout: 500, method: "notifyWhenChangesStop" } });
  954. self.isCurrentDirSelected = ko.computed(function () {
  955. return ko.utils.arrayFilter(self.files(), function (file) {
  956. return file.name == "." && file.selected();
  957. });
  958. }, self).extend({ rateLimit: { timeout: 500, method: "notifyWhenChangesStop" } });
  959. self.selectedFile = ko.computed(function () {
  960. return self.selectedFiles()[0];
  961. }, self).extend({ rateLimit: { timeout: 500, method: "notifyWhenChangesStop" } });
  962. self.isSelectedFileSql = ko.pureComputed(function() {
  963. return self.selectedFile().name.endsWith('.sql') || self.selectedFile().name.endsWith('.hql');
  964. });
  965. self.openFileInEditor = function() {
  966. huePubSub.publish('open.editor.new.query', {'statementType': 'file', 'statementPath': self.selectedFile().path});
  967. };
  968. self.currentPath = ko.observable(currentDirPath);
  969. self.currentPath.subscribe(function (path) {
  970. $(document).trigger('currPathLoaded', { path: path });
  971. });
  972. self.isS3 = ko.pureComputed(function () {
  973. return self.currentPath().toLowerCase().indexOf('s3a://') === 0;
  974. });
  975. self.isAdls = ko.pureComputed(function () {
  976. return self.currentPath().toLowerCase().indexOf('adl:/') === 0;
  977. });
  978. self.isABFS = ko.pureComputed(function () {
  979. return self.currentPath().toLowerCase().indexOf('abfs://') === 0;
  980. });
  981. self.scheme = ko.pureComputed(function () {
  982. var path = self.currentPath();
  983. return path.substring(0, path.indexOf(':/')) || "hdfs";
  984. });
  985. self.fs = ko.pureComputed(function () {
  986. var scheme = self.scheme();
  987. if (scheme === 'adl') {
  988. return 'adls';
  989. } else if (scheme === 's3a' ){
  990. return 's3';
  991. } else if (!scheme || scheme == 'hdfs') {
  992. return 'hdfs';
  993. } else {
  994. return scheme;
  995. }
  996. });
  997. function root(path) {
  998. var path = path && path.toLowerCase();
  999. if (path.indexOf('s3a://') >= 0) {
  1000. return 's3a://';
  1001. } else if (path.indexOf('adl:/') >= 0) {
  1002. return 'adl:/';
  1003. } else if (path.indexOf('abfs://') >= 0) {
  1004. return 'abfs://';
  1005. } else {
  1006. return '/';
  1007. }
  1008. };
  1009. self.rootCurrent = ko.pureComputed(function () {
  1010. return root(self.currentPath());
  1011. });
  1012. self.rootTarget = ko.pureComputed(function () {
  1013. return root(self.targetPath());
  1014. });
  1015. self.isHdfs = ko.pureComputed(function () {
  1016. var currentPath = self.currentPath().toLowerCase();
  1017. return currentPath.indexOf('/') === 0 || currentPath.indexOf('hdfs') === 0
  1018. });
  1019. self.isCompressEnabled = ko.pureComputed(function () {
  1020. return !self.isS3() && !self.isAdls() && !self.isABFS();
  1021. });
  1022. self.isSummaryEnabled = ko.pureComputed(function () {
  1023. return self.isHdfs();
  1024. });
  1025. self.isPermissionEnabled = ko.pureComputed(function () {
  1026. return !self.isS3() && !self.isABFS();
  1027. });
  1028. self.isReplicationEnabled = ko.pureComputed(function () {
  1029. return self.isHdfs();
  1030. });
  1031. self.isS3.subscribe(function (newVal) {
  1032. if (newVal) {
  1033. huePubSub.publish('update.autocompleters');
  1034. }
  1035. });
  1036. self.isS3Root = ko.pureComputed(function () {
  1037. return self.isS3() && self.currentPath().toLowerCase() === 's3a://';
  1038. });
  1039. self.isABFSRoot = ko.pureComputed(function () {
  1040. return self.isABFS() && self.currentPath().toLowerCase() === 'abfs://';
  1041. });
  1042. self.inTrash = ko.computed(function() {
  1043. return self.currentPath().match(/^\/user\/.+?\/\.Trash/);
  1044. });
  1045. self.inRestorableTrash = ko.computed(function() {
  1046. return self.currentPath().match(/^\/user\/.+?\/\.Trash\/.+?/);
  1047. });
  1048. self.isLoadingSummary = ko.observable(true);
  1049. self.contentSummary = ko.observable(ko.mapping.fromJS({
  1050. spaceConsumed: -1,
  1051. quota: -1,
  1052. spaceQuota: -1,
  1053. length: 0,
  1054. directoryCount: 0,
  1055. fileCount: 0,
  1056. replication: 0
  1057. }));
  1058. self.showSummary = function () {
  1059. self.isLoadingSummary(true);
  1060. $("#contentSummaryModal").modal("show");
  1061. $.getJSON("${url('content_summary', path='')}" + self.selectedFile().path, function (data) {
  1062. if (data.status == 0) {
  1063. self.contentSummary(ko.mapping.fromJS(data.summary));
  1064. self.isLoadingSummary(false);
  1065. } else {
  1066. $(document).trigger("error", data.message);
  1067. $("#contentSummaryModal").modal("hide");
  1068. }
  1069. }).fail(function (xhr, textStatus, errorThrown) {
  1070. $(document).trigger("error", xhr.responseText);
  1071. $("#contentSummaryModal").modal("hide");
  1072. });
  1073. }
  1074. self.getStats = function (callback) {
  1075. $.getJSON(self.targetPath() + (self.targetPath().indexOf('?') > 0 ? '&' : '?') + "pagesize=1&format=json", callback);
  1076. };
  1077. self.retrieveData = function (clearAssistCache) {
  1078. self.isLoading(true);
  1079. $.getJSON(self.targetPath() + (self.targetPath().indexOf('?') > 0 ? '&' : '?') + "pagesize=" + self.recordsPerPage() + "&pagenum=" + self.targetPageNum() + "&filter=" + self.searchQuery() + "&sortby=" + self.sortBy() + "&descending=" + self.sortDescending() + "&format=json", function (data) {
  1080. if (data.error){
  1081. $(document).trigger("error", data.error);
  1082. self.isLoading(false);
  1083. return false;
  1084. }
  1085. if (data.type != null && data.type == "file") {
  1086. huePubSub.publish('open.link', data.url);
  1087. return false;
  1088. }
  1089. self.updateFileList(data.files, data.page, data.breadcrumbs, data.current_dir_path, data.is_sentry_managed);
  1090. if (clearAssistCache) {
  1091. huePubSub.publish('assist.'+self.fs()+'.refresh');
  1092. }
  1093. if ($("#hueBreadcrumbText").is(":visible")) {
  1094. $(".hue-breadcrumbs").show();
  1095. $("#hueBreadcrumbText").hide();
  1096. $("#editBreadcrumb").show();
  1097. }
  1098. });
  1099. };
  1100. self.updateFileList = function (files, page, breadcrumbs, currentDirPath, isSentryManaged) {
  1101. $(".tooltip").hide();
  1102. self.isCurrentDirSentryManaged(isSentryManaged);
  1103. self.page(new Page(page));
  1104. self.files(ko.utils.arrayMap(files, function (file) {
  1105. file.highlighted = self.filesToHighlight.indexOf(file.path) > -1;
  1106. var f = new File(file);
  1107. window.setTimeout(function(){
  1108. f.highlighted(false);
  1109. }, 3000);
  1110. return f;
  1111. }));
  1112. self.filesToHighlight([]);
  1113. if (self.sortBy() == "name"){
  1114. self.files.sort(self.fileNameSorting);
  1115. }
  1116. self.breadcrumbs(ko.utils.arrayMap(breadcrumbs, function (breadcrumb) {
  1117. return new Breadcrumb(breadcrumb);
  1118. }));
  1119. self.currentPath(currentDirPath);
  1120. $('.uploader').trigger('fb:updatePath', {dest:self.currentPath()});
  1121. self.isLoading(false);
  1122. $("*[rel='tooltip']").tooltip({ placement:"left" });
  1123. var $scrollable = $(window);
  1124. %if is_embeddable:
  1125. $scrollable = $('.page-content');
  1126. %endif
  1127. if ($('.row-highlighted').length > 0) {
  1128. $scrollable.scrollTop($('.row-highlighted:eq(0)').offset().top - 150);
  1129. }
  1130. else if ($('.row-deleted').length > 0) {
  1131. $scrollable.scrollTop($('.row-deleted:eq(0)').offset().top - 150);
  1132. }
  1133. else {
  1134. $scrollable.scrollTop(0);
  1135. }
  1136. resetActionbar();
  1137. };
  1138. self.recordsPerPage.subscribe(function (newValue) {
  1139. apiHelper.setInTotalStorage('fb', 'records_per_page', newValue)
  1140. self.retrieveData();
  1141. });
  1142. self.skipTo = function () {
  1143. var doc = document,
  1144. old_page = doc.querySelector('#current_page').value,
  1145. page = doc.querySelector('.pagination-input');
  1146. if (! isNaN(page.value) && (page.value > 0 && page.value <= self.page().num_pages)) {
  1147. self.goToPage(page.value);
  1148. } else {
  1149. page.value = old_page;
  1150. }
  1151. };
  1152. self.goToPage = function (pageNumber) {
  1153. self.targetPageNum(pageNumber);
  1154. self.retrieveData();
  1155. };
  1156. self.firstPage = function () {
  1157. self.goToPage(1)
  1158. };
  1159. self.previousPage = function () {
  1160. self.goToPage(self.page().previous_page_number)
  1161. };
  1162. self.nextPage = function () {
  1163. self.goToPage(self.page().next_page_number)
  1164. };
  1165. self.lastPage = function () {
  1166. self.goToPage(self.page().num_pages)
  1167. };
  1168. self.selectAll = function () {
  1169. self.allSelected(!self.allSelected());
  1170. ko.utils.arrayForEach(self.files(), function (file) {
  1171. if (file.name != "." && file.name != "..") {
  1172. file.selected(self.allSelected());
  1173. }
  1174. });
  1175. return true;
  1176. };
  1177. self.searchQuery.subscribe(function (newValue) {
  1178. if (newValue !== '' || self.enableFilterAfterSearch) {
  1179. self.filter();
  1180. }
  1181. self.enableFilterAfterSearch = true;
  1182. });
  1183. self.filter = function () {
  1184. self.targetPageNum(1);
  1185. self.retrieveData();
  1186. };
  1187. self.openDefaultFolder = function (vm, e, folderPath) {
  1188. var isLeftButton = (e.which || e.button) === 1;
  1189. if (isLeftButton) {
  1190. if (! (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) {
  1191. e.stopPropagation();
  1192. e.preventDefault();
  1193. fileBrowserViewModel.targetPageNum(1);
  1194. fileBrowserViewModel.targetPath("${url('filebrowser.views.view', path='')}?" + folderPath);
  1195. updateHash('');
  1196. fileBrowserViewModel.retrieveData();
  1197. }
  1198. else {
  1199. window.open("${url('filebrowser.views.view', path='')}?" + folderPath);
  1200. }
  1201. }
  1202. }
  1203. self.openHome = function (vm, e) {
  1204. self.openDefaultFolder(vm, e, 'default_to_home');
  1205. }
  1206. self.openTrash = function (vm, e) {
  1207. self.openDefaultFolder(vm, e, 'default_to_trash');
  1208. }
  1209. self.viewFile = function (file, e) {
  1210. e.stopImmediatePropagation();
  1211. if (file.type == "dir") {
  1212. // Reset page number so that we don't hit a page that doesn't exist
  1213. self.targetPageNum(1);
  1214. self.enableFilterAfterSearch = false;
  1215. self.searchQuery("");
  1216. self.targetPath(file.url);
  1217. updateHash(stripHashes(file.path));
  1218. } else {
  1219. %if is_embeddable:
  1220. huePubSub.publish('open.link', file.url);
  1221. %else:
  1222. window.location.href = file.url;
  1223. %endif
  1224. }
  1225. };
  1226. self.editFile = function () {
  1227. huePubSub.publish('open.link', self.selectedFile().url.replace("${url('filebrowser.views.view', path='')}", "${url('filebrowser_views_edit', path='')}"));
  1228. };
  1229. self.downloadFile = function () {
  1230. huePubSub.publish('open.link', self.selectedFile().url.replace("${url('filebrowser.views.view', path='')}", "${url('filebrowser_views_download', path='')}"));
  1231. };
  1232. self.renameFile = function () {
  1233. $("#renameSrcPath").attr("value", self.selectedFile().path);
  1234. $("#renameFileName").text(self.selectedFile().path);
  1235. $("#newNameInput").val(self.selectedFile().name);
  1236. $("#renameForm").attr("action", "/filebrowser/rename?next=${url('filebrowser.views.view', path='')}" + self.currentPath());
  1237. $('#renameForm').ajaxForm({
  1238. dataType: 'json',
  1239. success: function() {
  1240. $("#renameModal").modal('hide');
  1241. self.filesToHighlight.push(self.currentPath() + '/' + $('#newNameInput').val());
  1242. self.retrieveData(true);
  1243. }
  1244. });
  1245. $("#renameModal").modal({
  1246. keyboard:true,
  1247. show:true
  1248. });
  1249. };
  1250. self.setReplicationFactor = function () {
  1251. $("#SrcPath").attr("value", self.selectedFile().path);
  1252. $("#setReplFileName").text(self.selectedFile().path);
  1253. $("#setReplicationFactorForm").attr("action", "/filebrowser/set_replication?next=${url('filebrowser.views.view', path='')}" + self.currentPath());
  1254. $('#setReplicationFactorForm').ajaxForm({
  1255. dataType: 'json',
  1256. success: function() {
  1257. $("#setReplicationModal").modal('hide');
  1258. $("#newRepFactorInput").val('');
  1259. self.retrieveData(true);
  1260. }
  1261. });
  1262. $("#setReplicationModal").modal({
  1263. keyboard:true,
  1264. show:true
  1265. }).on('shown', function(){
  1266. $('#newRepFactorInput').val(self.selectedFile().stats.replication);
  1267. });
  1268. };
  1269. self.move = function (mode, unselectedDrag) {
  1270. var paths = [];
  1271. var isMoveOnSelf = false;
  1272. $(self.selectedFiles()).each(function (index, file) {
  1273. if (file.path == $('#moveDestination').val()){
  1274. isMoveOnSelf = true;
  1275. }
  1276. paths.push(file.path);
  1277. });
  1278. if (paths.length == 0 && typeof unselectedDrag !== 'undefined'){
  1279. paths.push(_dragged.path);
  1280. }
  1281. if (!isMoveOnSelf){
  1282. hiddenFields($("#moveForm"), "src_path", paths);
  1283. $("#moveForm").attr("action", "/filebrowser/move?next=${url('filebrowser.views.view', path='')}" + self.currentPath());
  1284. $('#moveForm').ajaxForm({
  1285. dataType: 'json',
  1286. success: function() {
  1287. $("#moveModal").modal('hide');
  1288. self.filesToHighlight.push(self.currentPath() + '/' + $('#moveDestination').val());
  1289. self.retrieveData(true);
  1290. },
  1291. error: function(xhr){
  1292. $.jHueNotify.error(xhr.responseText);
  1293. resetPrimaryButtonsStatus();
  1294. $('#moveDestination').val('');
  1295. }
  1296. });
  1297. if (mode === 'nomodal') {
  1298. $.jHueNotify.info('${ _('Items moving to') } "' + $('#moveDestination').val() + '"');
  1299. $("#moveForm").submit();
  1300. } else {
  1301. $("#moveModal").modal({
  1302. keyboard: true,
  1303. show: true
  1304. });
  1305. $("#moveModal").on("shown", function () {
  1306. $("#moveDestination").val('');
  1307. $("#moveNameRequiredAlert").hide();
  1308. $("#moveForm").find("input[name='*dest_path']").removeClass("fieldError");
  1309. $("#moveModal .modal-footer div").show();
  1310. $("#moveFilechooser").remove();
  1311. $("<div>").attr("id", "moveFilechooser").appendTo($("#moveModal .modal-body"));
  1312. $("#moveFilechooser").jHueFileChooser({
  1313. initialPath: paths[0] || '',
  1314. filesystemsFilter: [self.scheme()],
  1315. onNavigate: function (filePath) {
  1316. $("#moveDestination").val(filePath);
  1317. },
  1318. createFolder: false,
  1319. uploadFile: false
  1320. });
  1321. });
  1322. }
  1323. }
  1324. else {
  1325. $.jHueNotify.warn("${ _('You cannot copy a folder into itself.') }");
  1326. $('#moveDestination').val('');
  1327. }
  1328. };
  1329. self.copy = function () {
  1330. var paths = [];
  1331. $(self.selectedFiles()).each(function (index, file) {
  1332. paths.push(file.path);
  1333. });
  1334. hiddenFields($("#copyForm"), "src_path", paths);
  1335. $("#copyForm").attr("action", "/filebrowser/copy?next=${url('filebrowser.views.view', path='')}" + self.currentPath());
  1336. $("#copyModal").modal({
  1337. keyboard:true,
  1338. show:true
  1339. });
  1340. $('#copyForm').ajaxForm({
  1341. dataType: 'json',
  1342. success: function() {
  1343. $("#copyModal").modal('hide');
  1344. self.filesToHighlight.push(self.currentPath() + '/' + $('#copyDestination').val());
  1345. self.retrieveData(true);
  1346. },
  1347. error: function(xhr){
  1348. $.jHueNotify.error(xhr.responseText);
  1349. resetPrimaryButtonsStatus();
  1350. }
  1351. });
  1352. $("#copyModal").on("shown", function () {
  1353. $("#copyDestination").val('');
  1354. $("#copyNameRequiredAlert").hide();
  1355. $("#copyForm").find("input[name='*dest_path']").removeClass("fieldError");
  1356. $("#copyModal .modal-footer div").show();
  1357. $("#copyFilechooser").remove();
  1358. $("<div>").attr("id", "copyFilechooser").appendTo($("#copyModal .modal-body"));
  1359. $("#copyFilechooser").jHueFileChooser({
  1360. initialPath: paths[0] || '',
  1361. filesystemsFilter: [self.scheme()],
  1362. onNavigate: function (filePath) {
  1363. $("#copyDestination").val(filePath);
  1364. },
  1365. createFolder: false,
  1366. uploadFile: false
  1367. });
  1368. });
  1369. };
  1370. self.changeOwner = function (data, event) {
  1371. if (!self.isCurrentDirSentryManaged()) {
  1372. var paths = [];
  1373. event.preventDefault();
  1374. event.stopPropagation();
  1375. $(self.selectedFiles()).each(function (index, file) {
  1376. paths.push(file.path);
  1377. });
  1378. hiddenFields($("#chownForm"), 'path', paths);
  1379. $("#chownForm").attr("action", "/filebrowser/chown?next=${url('filebrowser.views.view', path='')}" + self.currentPath());
  1380. $("select[name='user']").val(self.selectedFile().stats.user);
  1381. $("#chownForm input[name='group_other']").removeClass("fieldError");
  1382. $("#chownForm input[name='user_other']").removeClass("fieldError");
  1383. $("#chownRequired").hide();
  1384. if ($("select[name='group'] option:contains('" + self.selectedFile().stats.group + "')").length > 0) {
  1385. $("select[name='group']").val(self.selectedFile().stats.group);
  1386. } else {
  1387. $("select[name='group']").val("__other__");
  1388. $("input[name='group_other']").val(self.selectedFile().stats.group);
  1389. }
  1390. $("select[name='group']").change();
  1391. $("#changeOwnerModal").modal({
  1392. keyboard: true,
  1393. show: true
  1394. });
  1395. $('#chownForm').ajaxForm({
  1396. dataType: 'json',
  1397. success: function() {
  1398. $("#changeOwnerModal").modal('hide');
  1399. $(self.selectedFiles()).each(function (index, file) {
  1400. self.filesToHighlight.push(file.path);
  1401. });
  1402. self.retrieveData(true);
  1403. },
  1404. error: function (xhr, textStatus, errorThrown) {
  1405. $(document).trigger("error", xhr.responseText);
  1406. resetPrimaryButtonsStatus();
  1407. }
  1408. });
  1409. }
  1410. };
  1411. self.changePermissions = function (data, event) {
  1412. if (!self.isCurrentDirSentryManaged()) {
  1413. var paths = [];
  1414. event.preventDefault();
  1415. event.stopPropagation();
  1416. $(self.selectedFiles()).each(function (index, file) {
  1417. paths.push(file.path);
  1418. });
  1419. hiddenFields($("#chmodForm"), 'path', paths);
  1420. $("#chmodForm").attr("action", "/filebrowser/chmod?next=${url('filebrowser.views.view', path='')}" + self.currentPath());
  1421. $("#changePermissionModal").modal({
  1422. keyboard: true,
  1423. show: true
  1424. });
  1425. $('#chmodForm').ajaxForm({
  1426. dataType: 'json',
  1427. success: function() {
  1428. $("#changePermissionModal").modal('hide');
  1429. $(self.selectedFiles()).each(function (index, file) {
  1430. self.filesToHighlight.push(file.path);
  1431. });
  1432. self.retrieveData(true);
  1433. },
  1434. error: function (xhr, textStatus, errorThrown) {
  1435. $(document).trigger("error", xhr.responseText);
  1436. resetPrimaryButtonsStatus();
  1437. }
  1438. });
  1439. // Initial values for form
  1440. var permissions = ["sticky", "user_read", "user_write", "user_execute", "group_read", "group_write", "group_execute", "other_read", "other_write", "other_execute"].reverse();
  1441. var mode = octal(self.selectedFile().mode) & 01777;
  1442. for (var i = 0; i < permissions.length; i++) {
  1443. if (mode & 1) {
  1444. $("#chmodForm input[name='" + permissions[i] + "']").attr("checked", true);
  1445. } else {
  1446. $("#chmodForm input[name='" + permissions[i] + "']").attr("checked", false);
  1447. }
  1448. mode >>>= 1;
  1449. }
  1450. }
  1451. };
  1452. var deleteSelected = function() {
  1453. var paths = [];
  1454. $(self.selectedFiles()).each(function (index, file) {
  1455. paths.push(file.path);
  1456. });
  1457. hiddenFields($("#deleteForm"), 'path', paths);
  1458. $("#deleteForm").attr("action", "/filebrowser/rmtree" + "?" +
  1459. (self.skipTrash() ? "skip_trash=true&" : "") +
  1460. "next=${url('filebrowser.views.view', path='')}" + self.currentPath());
  1461. $("#deleteModal").modal({
  1462. keyboard:true,
  1463. show:true
  1464. });
  1465. $('#deleteForm').ajaxForm({
  1466. dataType: 'json',
  1467. success: function() {
  1468. $("#deleteModal").modal('hide');
  1469. window.setTimeout(function () {
  1470. $(self.selectedFiles()).each(function (index, file) {
  1471. file.deleted(true);
  1472. });
  1473. var $scrollable = $(window);
  1474. %if is_embeddable:
  1475. $scrollable = $('.page-content');
  1476. %endif
  1477. if ($('.row-deleted').length > 0 && $('.row-deleted:eq(0)').offset()) {
  1478. $scrollable.scrollTop($('.row-deleted:eq(0)').offset().top - 150);
  1479. }
  1480. }, 500);
  1481. window.setTimeout(function () {
  1482. $(self.selectedFiles()).each(function (index, file) {
  1483. self.files.remove(file);
  1484. });
  1485. self.retrieveData(true);
  1486. }, 1000);
  1487. },
  1488. error: function(xhr, textStatus, errorThrown) {
  1489. $(document).trigger("error", xhr.responseText);
  1490. resetPrimaryButtonsStatus();
  1491. }
  1492. });
  1493. };
  1494. self.deleteSelected = function () {
  1495. self.skipTrash(true);
  1496. deleteSelected();
  1497. };
  1498. self.trashSelected = function () {
  1499. self.skipTrash(false);
  1500. deleteSelected();
  1501. };
  1502. self.submitSelected = function() {
  1503. % if 'oozie' in apps:
  1504. $.get("${ url('oozie:submit_external_job', application_path='/') }../" + self.selectedFile().path, { 'format': 'json' }, function (response) {
  1505. $('#submit-wf-modal').html(response);
  1506. $('#submit-wf-modal').modal('show');
  1507. });
  1508. % else:
  1509. $.jHueNotify.warn("${ _('Submitting is not available as the Oozie app is disabled') }");
  1510. % endif
  1511. };
  1512. self.isArchive = function(fileName) {
  1513. return fileName.endsWith('.zip') || fileName.endsWith('.tar.gz') || fileName.endsWith('.tgz') || fileName.endsWith('.bz2') || fileName.endsWith('.bzip2');
  1514. };
  1515. self.confirmExtractArchive = function() {
  1516. $("#confirmExtractModal").modal({
  1517. keyboard:true,
  1518. show:true
  1519. });
  1520. };
  1521. self.showCompressButton = ko.computed(function() {
  1522. var fileNames = self.selectedFiles().map(function(file) {
  1523. return file.name;
  1524. });
  1525. if (fileNames.indexOf('.') !== -1) {
  1526. return false;
  1527. }
  1528. return self.isCompressEnabled() && (self.selectedFiles().length > 1 || !(self.selectedFiles().length === 1 && self.isArchive(self.selectedFile().name)));
  1529. });
  1530. self.setCompressArchiveDefault = function() {
  1531. if (self.selectedFiles().length == 1) {
  1532. self.compressArchiveName(self.selectedFile().name + '.zip');
  1533. } else {
  1534. if (self.breadcrumbs().length === 1) {
  1535. self.compressArchiveName('root.zip'); // When compressing multiple files in root directory
  1536. }
  1537. else {
  1538. self.compressArchiveName(self.breadcrumbs()[self.breadcrumbs().length - 1].label + '.zip'); // Setting to parent directory name
  1539. }
  1540. }
  1541. };
  1542. self.confirmCompressFiles = function() {
  1543. $("#confirmCompressModal").modal({
  1544. keyboard:true,
  1545. show:true
  1546. });
  1547. };
  1548. self.extractSelectedArchive = function() {
  1549. $("#confirmExtractModal").modal("hide");
  1550. $.post("/filebrowser/extract_archive", {
  1551. "archive_name": self.selectedFile().name,
  1552. "upload_path": self.currentPath(),
  1553. "start_time": ko.mapping.toJSON((new Date()).getTime())
  1554. }, function (data) {
  1555. if (data.status == 0) {
  1556. $.jHueNotify.info("${ _('Task ') }" + data.history_uuid + "${_(' submitted.') }");
  1557. huePubSub.publish('notebook.task.submitted', data.history_uuid);
  1558. } else {
  1559. $(document).trigger("error", data.message);
  1560. }
  1561. }).fail(function (xhr, textStatus, errorThrown) {
  1562. $(document).trigger("error", xhr.responseText);
  1563. });
  1564. };
  1565. self.archiveOverrideWarning = function() {
  1566. $("#confirmCompressModal").modal("hide");
  1567. var fileNames = self.files().map(function(file) {
  1568. return file.name;
  1569. });
  1570. if (fileNames.indexOf(self.compressArchiveName()) !== -1) {
  1571. $("#compressWarningModal").modal("show");
  1572. } else {
  1573. self.compressSelectedFiles();
  1574. }
  1575. }
  1576. self.compressSelectedFiles = function() {
  1577. $("#compressWarningModal").modal("hide");
  1578. var fileNames = [];
  1579. $(self.selectedFiles()).each(function (index, file) {
  1580. fileNames.push(file.name);
  1581. });
  1582. $.post("/filebrowser/compress_files", {
  1583. "files": fileNames,
  1584. "upload_path": self.currentPath(),
  1585. "archive_name": self.compressArchiveName(),
  1586. "start_time": ko.mapping.toJSON((new Date()).getTime())
  1587. }, function (data) {
  1588. if (data.status == 0) {
  1589. $.jHueNotify.info("${ _('Task ') }" + data.history_uuid + "${_(' submitted.') }");
  1590. huePubSub.publish('notebook.task.submitted', data.history_uuid);
  1591. } else {
  1592. $(document).trigger("error", data.message);
  1593. }
  1594. }).fail(function (xhr, textStatus, errorThrown) {
  1595. $(document).trigger("error", xhr.responseText);
  1596. });
  1597. };
  1598. self.createDirectory = function (formElement) {
  1599. $(formElement).attr("action", "/filebrowser/mkdir?next=${url('filebrowser.views.view', path='')}" + self.currentPath());
  1600. if ($.trim($("#newDirectoryNameInput").val()) == "") {
  1601. $("#directoryNameRequiredAlert").show();
  1602. $("#newDirectoryNameInput").addClass("fieldError");
  1603. resetPrimaryButtonsStatus(); //globally available
  1604. return false;
  1605. }
  1606. if (fileExists($("#newDirectoryNameInput").val())) {
  1607. $("#directoryNameExistsAlert").find(".newName").text($("#newDirectoryNameInput").val());
  1608. $("#directoryNameExistsAlert").show();
  1609. $("#newDirectoryNameInput").addClass("fieldError");
  1610. resetPrimaryButtonsStatus(); //globally available
  1611. return false;
  1612. }
  1613. if ($("#newDirectoryNameInput").val().length < 3 && self.isABFSRoot()) {
  1614. $("#smallFileSystemNameAlert").show();
  1615. $("#newDirectoryNameInput").addClass("fieldError");
  1616. resetPrimaryButtonsStatus(); //globally available
  1617. return false;
  1618. }
  1619. $(formElement).ajaxSubmit({
  1620. dataType: 'json',
  1621. success: function() {
  1622. self.filesToHighlight.push(self.currentPath() + '/' + $('#newDirectoryNameInput').val());
  1623. $("#createDirectoryModal").modal('hide');
  1624. self.retrieveData(true);
  1625. },
  1626. error: function (xhr, textStatus, errorThrown) {
  1627. $(document).trigger("error", xhr.responseText);
  1628. resetPrimaryButtonsStatus();
  1629. }
  1630. });
  1631. return false;
  1632. };
  1633. self.createFile = function (formElement) {
  1634. $(formElement).attr("action", "/filebrowser/touch?next=${url('filebrowser.views.view', path='')}" + self.currentPath());
  1635. if ($.trim($("#newFileNameInput").val()) == "") {
  1636. $("#fileNameRequiredAlert").show();
  1637. $("#newFileNameInput").addClass("fieldError");
  1638. resetPrimaryButtonsStatus(); //globally available
  1639. return false;
  1640. }
  1641. if (fileExists($("#newFileNameInput").val())) {
  1642. $("#fileNameExistsAlert").find(".newName").text($("#newFileNameInput").val());
  1643. $("#fileNameExistsAlert").show();
  1644. $("#newFileNameInput").addClass("fieldError");
  1645. resetPrimaryButtonsStatus(); //globally available
  1646. return false;
  1647. }
  1648. $(formElement).ajaxSubmit({
  1649. dataType: 'json',
  1650. success: function() {
  1651. self.filesToHighlight.push(self.currentPath() + '/' + $('#newFileNameInput').val());
  1652. $("#createFileModal").modal('hide');
  1653. self.retrieveData(true);
  1654. },
  1655. error: function (xhr, textStatus, errorThrown) {
  1656. $(document).trigger("error", xhr.responseText);
  1657. resetPrimaryButtonsStatus();
  1658. }
  1659. });
  1660. return false;
  1661. };
  1662. self.restoreTrashSelected = function(formElement) {
  1663. var paths = [];
  1664. $(self.selectedFiles()).each(function (index, file) {
  1665. paths.push(file.path);
  1666. });
  1667. hiddenFields($("#restoreTrashForm"), 'path', paths);
  1668. $("#restoreTrashForm").attr("action", "/filebrowser/trash/restore?next=${url('filebrowser.views.view', path='')}" + self.currentPath());
  1669. $("#restoreTrashModal").modal({
  1670. keyboard:true,
  1671. show:true
  1672. });
  1673. $('#restoreTrashForm').ajaxForm({
  1674. dataType: 'json',
  1675. success: function() {
  1676. $("#restoreTrashModal").modal('hide');
  1677. self.retrieveData(true);
  1678. },
  1679. error: function(xhr, textStatus, errorThrown) {
  1680. $(document).trigger("error", xhr.responseText);
  1681. resetPrimaryButtonsStatus();
  1682. }
  1683. });
  1684. };
  1685. self.purgeTrash = function(formElement) {
  1686. var paths = [];
  1687. $(self.selectedFiles()).each(function (index, file) {
  1688. paths.push(file.path);
  1689. });
  1690. hiddenFields($("#purgeTrashForm"), 'path', paths);
  1691. $("#purgeTrashForm").attr("action", "/filebrowser/trash/purge?next=${url('filebrowser.views.view', path='')}" + fileBrowserViewModel.homeDir() + "/.Trash");
  1692. $("#purgeTrashModal").modal({
  1693. keyboard:true,
  1694. show:true
  1695. });
  1696. $('#purgeTrashForm').ajaxForm({
  1697. dataType: 'json',
  1698. success: function() {
  1699. $("#purgeTrashModal").modal('hide');
  1700. self.retrieveData(true);
  1701. }
  1702. });
  1703. };
  1704. self.uploadFile = (function () {
  1705. self.pendingUploads(0);
  1706. var action = "/filebrowser/upload/file";
  1707. var uploader = new qq.FileUploader({
  1708. element: document.getElementById("fileUploader"),
  1709. action: action,
  1710. template: '<div class="qq-uploader" style="margin-left: 10px">' +
  1711. '<div class="qq-upload-drop-area"><span>${_('Drop the files here to upload')}</span></div>' +
  1712. '<div class="qq-upload-button qq-no-float">${_('Select files')}</div> &nbsp; <span class="muted">${_('or drag and drop them here')}</span>' +
  1713. '<ul class="qq-upload-list qq-upload-files unstyled qq-no-float" style="margin-right: 0;"></ul>' +
  1714. '</div>',
  1715. fileTemplate: '<li><span class="qq-upload-file-extended" style="display:none"></span><span class="qq-upload-spinner hide" style="display:none"></span>' +
  1716. '<div class="progress-row dz-processing">' +
  1717. '<span class="break-word qq-upload-file"></span>' +
  1718. '<div class="pull-right">' +
  1719. '<span class="muted qq-upload-size"></span>&nbsp;&nbsp;' +
  1720. '<a href="#" title="${_('Cancel')}" class="complex-layout"><i class="fa fa-fw fa-times qq-upload-cancel"></i></a>' +
  1721. '<span class="qq-upload-done" style="display:none"><i class="fa fa-fw fa-check muted"></i></span>' +
  1722. '<span class="qq-upload-failed-text">${_('Failed')}</span>' +
  1723. '</div>' +
  1724. '<div class="progress-row-bar" style="width: 0%;"></div>' +
  1725. '</div></li>',
  1726. params: {
  1727. dest: self.currentPath(),
  1728. fileFieldLabel: "hdfs_file"
  1729. },
  1730. onProgress: function (id, fileName, loaded, total) {
  1731. $('.qq-upload-files').find('li').each(function(){
  1732. var listItem = $(this);
  1733. if (listItem.find('.qq-upload-file-extended').text() == fileName){
  1734. listItem.find('.progress-row-bar').css('width', (loaded/total)*100 + '%');
  1735. }
  1736. });
  1737. },
  1738. onComplete: function (id, fileName, response) {
  1739. self.pendingUploads(self.pendingUploads() - 1);
  1740. if (response.status != 0) {
  1741. $(document).trigger('error', "${ _('Error: ') }" + response.data);
  1742. }
  1743. else {
  1744. $(document).trigger('info', response.path + "${ _(' uploaded successfully.') }");
  1745. self.filesToHighlight.push(response.path);
  1746. }
  1747. if (self.pendingUploads() == 0) {
  1748. $('#uploadFileModal').modal('hide');
  1749. self.retrieveData(true);
  1750. }
  1751. },
  1752. onSubmit: function (id, fileName, responseJSON) {
  1753. self.pendingUploads(self.pendingUploads() + 1);
  1754. },
  1755. onCancel: function (id, fileName) {
  1756. self.pendingUploads(self.pendingUploads() - 1);
  1757. },
  1758. debug: false
  1759. });
  1760. $("#fileUploader").on('fb:updatePath', function (e, options) {
  1761. uploader.setParams({
  1762. dest: options.dest,
  1763. fileFieldLabel: "hdfs_file"
  1764. });
  1765. });
  1766. return function () {
  1767. $("#uploadFileModal").modal({
  1768. show: true
  1769. });
  1770. };
  1771. })();
  1772. // Place all values into hidden fields under parent element.
  1773. // Looks for managed hidden fields and handles sizing appropriately.
  1774. var hiddenFields = function (parentEl, name, values) {
  1775. if (!(parentEl instanceof jQuery)){
  1776. parentEl = $(parentEl);
  1777. }
  1778. parentEl.find("input.hidden-field").remove();
  1779. $(values).each(function (index, value) {
  1780. var field = $("<input type='hidden' />");
  1781. field.attr("name", name);
  1782. field.attr("class", "hidden-field")
  1783. field.val(value);
  1784. parentEl.append(field);
  1785. });
  1786. };
  1787. var octal = function (strInt) {
  1788. return parseInt("0" + strInt, 8);
  1789. };
  1790. };
  1791. // hide the context menu based on specific events
  1792. var hideContextMenu = function () {
  1793. var cm = $('.context-menu');
  1794. if (cm.is(':visible')) {
  1795. cm.css({ display: 'none' });
  1796. }
  1797. };
  1798. var fileBrowserViewModel = new FileBrowserModel([], null, [], "/");
  1799. ko.applyBindings(fileBrowserViewModel, $('.filebrowser')[0]);
  1800. $(document).ready(function () {
  1801. // hide context menu
  1802. $('body').on('click', function (e) {
  1803. hideContextMenu();
  1804. });
  1805. $('body').on('contextmenu', function (e) {
  1806. if ($.inArray(e.toElement, $('.table-huedatatable *')) === -1) {
  1807. hideContextMenu();
  1808. }
  1809. });
  1810. $('body').on('contextmenu', '.context-menu', function (e) {
  1811. hideContextMenu();
  1812. });
  1813. % if show_upload_button:
  1814. // Drag and drop uploads from anywhere on filebrowser screen
  1815. if (window.FileReader) {
  1816. var showHoverMsg = function (msg) {
  1817. $('.filebrowser .hoverText').html(msg);
  1818. $('.filebrowser .hoverMsg').removeClass('hide');
  1819. };
  1820. var hideHoverMsg = function () {
  1821. $('.filebrowser .hoverMsg').addClass('hide');
  1822. };
  1823. var _isDraggingOverText = false,
  1824. // determine if the item dragged originated outside DOM
  1825. _isExternalFile = true;
  1826. $('.filebrowser').on('dragstart', function (e) {
  1827. // External files being dragged into the DOM won't have a dragstart event
  1828. _isExternalFile = false;
  1829. });
  1830. $('.filebrowser').on('dragend', function (e) {
  1831. _isExternalFile = true;
  1832. });
  1833. $('.filebrowser').on('dragenter', function (e) {
  1834. e.preventDefault();
  1835. if (_isExternalFile && !($("#uploadFileModal").is(":visible")) && (!fileBrowserViewModel.isS3() || (fileBrowserViewModel.isS3() && !fileBrowserViewModel.isS3Root()))) {
  1836. showHoverMsg("${_('Drop files here to upload')}");
  1837. }
  1838. });
  1839. $('.filebrowser .hoverText').on('dragenter', function (e) {
  1840. e.preventDefault();
  1841. e.stopPropagation();
  1842. e.stopImmediatePropagation();
  1843. _isDraggingOverText = true;
  1844. });
  1845. $('.filebrowser .hoverText').on('dragleave', function (e) {
  1846. e.preventDefault();
  1847. e.stopPropagation();
  1848. e.stopImmediatePropagation();
  1849. _isDraggingOverText = false;
  1850. _isExternalFile = true;
  1851. });
  1852. $('.filebrowser .hoverMsg').on('dragleave', function (e) {
  1853. if (!_isDraggingOverText) {
  1854. hideHoverMsg();
  1855. }
  1856. });
  1857. $(document).on('currPathLoaded', function (e, ops) {
  1858. try {
  1859. _dropzone.destroy();
  1860. }
  1861. catch (exc) {
  1862. }
  1863. var options = {
  1864. url: '/filebrowser/upload/file?dest=' + encodeURI(ops.path),
  1865. paramName: 'hdfs_file',
  1866. params: {
  1867. dest: ops.path
  1868. },
  1869. autoDiscover: false,
  1870. maxFilesize: 5000000,
  1871. previewsContainer: '#progressStatusContent',
  1872. previewTemplate: '<div class="progress-row">' +
  1873. '<span class="break-word" data-dz-name></span>' +
  1874. '<div class="pull-right">' +
  1875. '<span class="muted" data-dz-size></span>&nbsp;&nbsp;' +
  1876. '<span data-dz-remove><a href="javascript:undefined;" title="' + I18n('Cancel upload') + '"><i class="fa fa-fw fa-times"></i></a></span>' +
  1877. '<span style="display: none" data-dz-uploaded><i class="fa fa-fw fa-check muted"></i></span>' +
  1878. '</div>' +
  1879. '<div class="progress-row-bar" data-dz-uploadprogress></div>' +
  1880. '</div>',
  1881. drop: function (e) {
  1882. $('.filebrowser .hoverMsg').addClass('hide');
  1883. // Ensure dropped item was a file
  1884. if (e.dataTransfer.files.length > 0) {
  1885. $('#progressStatus').removeClass('hide');
  1886. $('#progressStatusBar').removeClass('hide');
  1887. $('#progressStatus .progress-row').remove();
  1888. $('#progressStatusBar div').css('width', '0');
  1889. }
  1890. },
  1891. processing: function (file) {
  1892. var newDest = ops.path;
  1893. if (file.fullPath) {
  1894. newDest = ops.path + '/' + file.fullPath.substr(0, file.fullPath.length - file.name.length);
  1895. }
  1896. this.options.params.dest = newDest;
  1897. this.options.url = '/filebrowser/upload/file?dest=' + encodeURI(newDest);
  1898. },
  1899. uploadprogress: function (file, progress) {
  1900. $('[data-dz-name]').each(function (cnt, item) {
  1901. if ($(item).text() === file.name) {
  1902. $(item).parents('.progress-row').find('[data-dz-uploadprogress]').width(progress.toFixed() + '%');
  1903. if (progress.toFixed() === '100') {
  1904. $(item).parents('.progress-row').find('[data-dz-remove]').hide();
  1905. $(item).parents('.progress-row').find('[data-dz-uploaded]').show();
  1906. }
  1907. }
  1908. });
  1909. },
  1910. totaluploadprogress: function (progress) {
  1911. $('#progressStatusBar div').width(progress.toFixed() + "%");
  1912. },
  1913. canceled: function () {
  1914. $.jHueNotify.info(I18n('The upload has been canceled'));
  1915. },
  1916. complete: function (data) {
  1917. if (data.xhr.response != '') {
  1918. var response = JSON.parse(data.xhr.response);
  1919. if (response && response.status != null) {
  1920. if (response.status != 0) {
  1921. $(document).trigger('error', response.data);
  1922. } else {
  1923. $(document).trigger('info', response.path + ' ' + I18n('uploaded successfully'));
  1924. fileBrowserViewModel.filesToHighlight.push(response.path);
  1925. }
  1926. }
  1927. }
  1928. }
  1929. };
  1930. if (ops.path.toLowerCase() !== 's3a://') {
  1931. _dropzone = new Dropzone($('.filebrowser .hoverMsg')[0], options);
  1932. _dropzone.on('queuecomplete', function () {
  1933. setTimeout(function () {
  1934. $('#progressStatus').addClass('hide');
  1935. $('#progressStatusBar').addClass('hide');
  1936. $('#progressStatusBar div').css("width", "0");
  1937. fileBrowserViewModel.retrieveData(true);
  1938. },
  1939. 2500);
  1940. });
  1941. }
  1942. });
  1943. }
  1944. % endif
  1945. $("#chownForm select[name='user']").change(function () {
  1946. if ($(this).val() == "__other__") {
  1947. $("input[name='user_other']").show();
  1948. } else {
  1949. $("input[name='user_other']").hide();
  1950. }
  1951. });
  1952. $("#chownForm select[name='group']").change(function () {
  1953. if ($(this).val() == "__other__") {
  1954. $("input[name='group_other']").show();
  1955. } else {
  1956. $("input[name='group_other']").hide();
  1957. }
  1958. });
  1959. $("#chownForm").submit(function () {
  1960. if ($("#chownForm select[name='user']").val() == null) {
  1961. $("#chownRequired").find(".label").text("${_('User is required.')}");
  1962. $("#chownRequired").show();
  1963. resetPrimaryButtonsStatus(); //globally available
  1964. return false;
  1965. } else if ($("#chownForm select[name='group']").val() == null) {
  1966. $("#chownRequired").find(".label").text("${_('Group is required.')}");
  1967. $("#chownRequired").show();
  1968. resetPrimaryButtonsStatus(); //globally available
  1969. return false;
  1970. } else {
  1971. if ($("#chownForm select[name='group']").val() == "__other__" && $("input[name='group_other']").val() == "") {
  1972. $("#chownRequired").find(".label").text("${_('Specify another group.')}");
  1973. $("#chownForm input[name='group_other']").addClass("fieldError");
  1974. $("#chownRequired").show();
  1975. resetPrimaryButtonsStatus(); //globally available
  1976. return false;
  1977. }
  1978. if ($("#chownForm select[name='user']").val() == "__other__" && $("input[name='user_other']").val() == "") {
  1979. $("#chownRequired").find(".label").text("${_('Specify another user.')}");
  1980. $("#chownForm input[name='user_other']").addClass("fieldError");
  1981. $("#chownRequired").show();
  1982. return false;
  1983. resetPrimaryButtonsStatus(); //globally available
  1984. }
  1985. return true;
  1986. }
  1987. });
  1988. // Modal file chooser
  1989. // The file chooser should be at least 2 levels deeper than the modal container
  1990. $(".fileChooserBtn").on('click', function (e) {
  1991. e.preventDefault();
  1992. var _destination = $(this).attr("data-filechooser-destination");
  1993. var fileChooser = $(this).parent().parent().find(".fileChooserModal");
  1994. fileChooser.jHueFileChooser({
  1995. initialPath:$("input[name='" + _destination + "']").val(),
  1996. onFolderChange:function (folderPath) {
  1997. $("input[name='" + _destination + "']").val(folderPath);
  1998. },
  1999. onFolderChoose:function (folderPath) {
  2000. $("input[name='" + _destination + "']").val(folderPath);
  2001. fileChooser.slideUp();
  2002. },
  2003. selectFolder:true,
  2004. createFolder:true,
  2005. uploadFile:false
  2006. });
  2007. fileChooser.slideDown();
  2008. });
  2009. $("#renameForm").submit(function () {
  2010. if ($("#newNameInput").val() == "") {
  2011. $("#renameNameRequiredAlert").show();
  2012. $("#newNameInput").addClass("fieldError");
  2013. resetPrimaryButtonsStatus(); //globally available
  2014. return false;
  2015. }
  2016. if (fileExists($("#newNameInput").val())) {
  2017. $("#renameNameExistsAlert").find(".newName").text($("#newNameInput").val());
  2018. $("#renameNameExistsAlert").show();
  2019. $("#newNameInput").addClass("fieldError");
  2020. resetPrimaryButtonsStatus(); //globally available
  2021. return false;
  2022. }
  2023. return true;
  2024. });
  2025. $("#newNameInput").focus(function () {
  2026. $("#renameNameRequiredAlert").hide();
  2027. $("#renameNameExistsAlert").hide();
  2028. $("#newNameInput").removeClass("fieldError");
  2029. });
  2030. $("#moveForm").on("submit", function () {
  2031. if ($.trim($("#moveDestination").val()) == "") {
  2032. $("#moveNameRequiredAlert").show();
  2033. $("#moveForm").find("input[name='*dest_path']").addClass("fieldError");
  2034. resetPrimaryButtonsStatus(); //globally available
  2035. return false;
  2036. }
  2037. var isMoveOnSelf = false;
  2038. $(fileBrowserViewModel.selectedFiles()).each(function (index, file) {
  2039. if (file.path == $('#moveDestination').val()) {
  2040. isMoveOnSelf = true;
  2041. return false;
  2042. }
  2043. });
  2044. if(isMoveOnSelf){
  2045. $.jHueNotify.warn("${ _('You cannot copy a folder into itself.') }");
  2046. $('#moveDestination').val('');
  2047. return false;
  2048. }
  2049. return true;
  2050. });
  2051. $("#moveForm").bind("keypress", function(e) {
  2052. if (e.keyCode == 13) {
  2053. return false;
  2054. }
  2055. });
  2056. $("#setReplicationFactorForm").submit(function () {
  2057. if ($("#newRepFactorInput").val() == "") {
  2058. $("#replicationFactorRequiredAlert").show();
  2059. $("#newRepFactorInput").addClass("fieldError");
  2060. resetPrimaryButtonsStatus(); //globally available
  2061. return false;
  2062. }
  2063. });
  2064. $("#newRepFactorInput").focus(function () {
  2065. $("#replicationFactorRequiredAlert").hide();
  2066. $("#newRepFactorInput").removeClass("fieldError");
  2067. });
  2068. huePubSub.subscribe('fb.' + fileBrowserViewModel.fs() + '.refresh', function (path) {
  2069. if (path === fileBrowserViewModel.currentPath()) {
  2070. fileBrowserViewModel.retrieveData();
  2071. }
  2072. }, 'filebrowser');
  2073. huePubSub.subscribe('update.autocompleters', function(){
  2074. $("#moveDestination").jHueHdfsAutocomplete({
  2075. showOnFocus: true,
  2076. skipEnter: true,
  2077. skipKeydownEvents: true,
  2078. onEnter: function (el) {
  2079. $("#jHueHdfsAutocomplete").hide();
  2080. },
  2081. isS3: fileBrowserViewModel.isS3(),
  2082. root: fileBrowserViewModel.rootCurrent()
  2083. });
  2084. $("#copyDestination").jHueHdfsAutocomplete({
  2085. showOnFocus: true,
  2086. skipEnter: true,
  2087. skipKeydownEvents: true,
  2088. onEnter: function (el) {
  2089. $("#jHueHdfsAutocomplete").hide();
  2090. },
  2091. isS3: fileBrowserViewModel.isS3(),
  2092. root: fileBrowserViewModel.rootCurrent()
  2093. });
  2094. });
  2095. huePubSub.subscribe('submit.popup.return', function (data) {
  2096. if (data.type == 'external_workflow') {
  2097. $.jHueNotify.info('${_('Workflow submitted.')}');
  2098. huePubSub.publish('open.link', '/jobbrowser/#!id=' + data.job_id);
  2099. huePubSub.publish('browser.job.open.link', data.job_id);
  2100. $('.submit-modal').modal('hide');
  2101. $('.modal-backdrop').hide();
  2102. }
  2103. }, 'filebrowser');
  2104. $("#copyForm").on("submit", function () {
  2105. if ($.trim($("#copyDestination").val()) == "") {
  2106. $("#copyNameRequiredAlert").show();
  2107. $("#copyForm").find("input[name='*dest_path']").addClass("fieldError");
  2108. resetPrimaryButtonsStatus(); //globally available
  2109. return false;
  2110. }
  2111. return true;
  2112. });
  2113. $("#copyForm").bind("keypress", function(e) {
  2114. if (e.keyCode == 13) {
  2115. return false;
  2116. }
  2117. });
  2118. huePubSub.publish('update.autocompleters');
  2119. $(".create-directory-link").click(function () {
  2120. $("#newDirectoryNameInput").val('');
  2121. $("#createDirectoryModal").modal({
  2122. keyboard:true,
  2123. show:true
  2124. });
  2125. });
  2126. $(".create-file-link").click(function () {
  2127. $("#newFileNameInput").val('');
  2128. $("#createFileModal").modal({
  2129. keyboard:true,
  2130. show:true
  2131. });
  2132. });
  2133. $("#newDirectoryNameInput").focus(function () {
  2134. $("#newDirectoryNameInput").removeClass("fieldError");
  2135. $("#directoryNameRequiredAlert").hide();
  2136. $("#directoryNameExistsAlert").hide();
  2137. $("#smallFileSystemNameAlert").hide();
  2138. });
  2139. $("#newFileNameInput").focus(function () {
  2140. $("#newFileNameInput").removeClass("fieldError");
  2141. $("#fileNameRequiredAlert").hide();
  2142. $("#fileNameExistsAlert").hide();
  2143. });
  2144. $(".pathChooser").click(function () {
  2145. var self = this;
  2146. $("#fileChooserRename").jHueFileChooser({
  2147. initialPath:$(self).val(),
  2148. onFileChoose:function (filePath) {
  2149. $(self).val(filePath);
  2150. },
  2151. onFolderChange:function (folderPath) {
  2152. $(self).val(folderPath);
  2153. },
  2154. createFolder:false,
  2155. uploadFile:false
  2156. });
  2157. $("#fileChooserRename").slideDown();
  2158. });
  2159. $("*[rel='tooltip']").tooltip({ placement:"bottom" });
  2160. var hashchange = function () {
  2161. if (window.location.pathname.indexOf('/filebrowser') > -1) {
  2162. var targetPath = "";
  2163. var hash = decodeURI(window.location.hash.substring(1));
  2164. if (hash != null && hash != "" && hash.indexOf('/') > -1) {
  2165. targetPath = "${url('filebrowser.views.view', path='')}";
  2166. if (hash.indexOf("!!") != 0) {
  2167. targetPath += stripHashes(encodeURIComponent(hash));
  2168. }
  2169. else {
  2170. targetPath = fileBrowserViewModel.targetPath() + encodeURI(hash);
  2171. }
  2172. fileBrowserViewModel.targetPageNum(1)
  2173. }
  2174. if (window.location.href.indexOf("#") == -1) {
  2175. fileBrowserViewModel.targetPageNum(1);
  2176. targetPath = "${current_request_path | n,unicode }";
  2177. }
  2178. if (targetPath != "") {
  2179. fileBrowserViewModel.targetPath(targetPath);
  2180. fileBrowserViewModel.retrieveData();
  2181. }
  2182. }
  2183. }
  2184. huePubSub.subscribe('fb.update.hash', hashchange, 'filebrowser');
  2185. if (window.location.hash != null && window.location.hash.length > 1) {
  2186. hashchange();
  2187. }
  2188. else {
  2189. fileBrowserViewModel.retrieveData();
  2190. }
  2191. $("#editBreadcrumb").click(function (e) {
  2192. if ($(e.target).is('ul')){
  2193. $(this).hide();
  2194. $(".hue-breadcrumbs").hide();
  2195. $("#hueBreadcrumbText").show().focus();
  2196. }
  2197. });
  2198. $("#hueBreadcrumbText").jHueHdfsAutocomplete({
  2199. home: "/user/${ user }/",
  2200. root: fileBrowserViewModel.rootTarget(),
  2201. skipKeydownEvents: true,
  2202. onEnter: function (el) {
  2203. fileBrowserViewModel.targetPath("${url('filebrowser.views.view', path='')}" + stripHashes(el.val()));
  2204. fileBrowserViewModel.getStats(function (data) {
  2205. if (data.type != null && data.type == "file") {
  2206. %if is_embeddable:
  2207. huePubSub.publish('open.link', data.url);
  2208. %else:
  2209. huePubSub.publish('open.link', data.url);
  2210. %endif
  2211. return false;
  2212. } else {
  2213. updateHash(stripHashes(el.val()));
  2214. }
  2215. $("#jHueHdfsAutocomplete").hide();
  2216. });
  2217. },
  2218. onBlur: function() {
  2219. $("#hueBreadcrumbText").hide();
  2220. $(".hue-breadcrumbs").show();
  2221. $("#editBreadcrumb").show();
  2222. },
  2223. smartTooltip: "${_('Did you know? You can use the tab key or CTRL + Space to autocomplete file and folder names')}"
  2224. });
  2225. $.ajaxSetup({
  2226. error:function (x, e) {
  2227. if (x.status == 500 && x.responseText.indexOf("jobbrowser") == -1) {
  2228. if (x.responseJSON && x.responseJSON.detail) {
  2229. $(document).trigger("error", x.responseJSON.detail);
  2230. }
  2231. else {
  2232. $(document).trigger("error", "${_('There was a problem with your request.')}");
  2233. }
  2234. $("#hueBreadcrumbText").blur();
  2235. }
  2236. }
  2237. });
  2238. $(window).bind("hashchange.fblist", hashchange);
  2239. $(".actionbar").data("originalWidth", $(".actionbar").width());
  2240. $(".actionbarGhost").height($(".actionbar").outerHeight());
  2241. resetActionbar();
  2242. %if is_embeddable:
  2243. $('.page-content').scroll(function () {
  2244. if ($('.page-content').scrollTop() > 50) {
  2245. $(".actionbar").width($(".actionbar").data("originalWidth"));
  2246. $(".actionbar").css("position", "fixed").css("top", "50px").css("zIndex", "1001");
  2247. $(".actionbarGhost").removeClass("hide");
  2248. } else {
  2249. resetActionbar();
  2250. }
  2251. });
  2252. %else:
  2253. $(window).scroll(function () {
  2254. if ($(window).scrollTop() > 20) {
  2255. $(".actionbar").width($(".actionbar").data("originalWidth"));
  2256. $(".actionbar").css("position", "fixed").css("top", "73px").css("zIndex", "1001");
  2257. $(".actionbarGhost").removeClass("hide");
  2258. } else {
  2259. resetActionbar();
  2260. }
  2261. });
  2262. %endif
  2263. $("#uploadFileModal").on("shown", function () {
  2264. if (typeof _dropzone != "undefined") {
  2265. _dropzone.disable();
  2266. }
  2267. });
  2268. $("#uploadFileModal").on("hidden", function () {
  2269. if (typeof _dropzone != "undefined") {
  2270. _dropzone.enable();
  2271. }
  2272. $(".qq-upload-list").empty();
  2273. $(".qq-upload-drop-area").hide();
  2274. });
  2275. });
  2276. </script>
  2277. </%def>