metastore.mako 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319
  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. from django.utils.html import escape
  18. from django.utils.translation import ugettext as _
  19. from desktop import conf
  20. from desktop.conf import USE_NEW_EDITOR
  21. from desktop.lib.i18n import smart_unicode
  22. from desktop.views import commonheader, commonfooter, _ko
  23. from beeswax.conf import LIST_PARTITIONS_LIMIT
  24. from metastore.conf import ENABLE_NEW_CREATE_TABLE
  25. %>
  26. <%namespace name="actionbar" file="actionbar.mako" />
  27. <%namespace name="assist" file="/assist.mako" />
  28. <%namespace name="components" file="components.mako" />
  29. <%
  30. MAIN_SCROLLABLE = is_embeddable and ".page-content" or ".content-panel"
  31. %>
  32. % if not is_embeddable:
  33. ${ commonheader(_("Metastore"), app_name, user, request) | n,unicode }
  34. <script src="${ static('desktop/ext/js/jquery/plugins/jquery-ui-1.10.4.custom.min.js') }"></script>
  35. <script src="${ static('desktop/ext/js/jquery/plugins/jquery.mousewheel.min.js') }"></script>
  36. <script src="${ static('desktop/ext/js/selectize.min.js') }"></script>
  37. <script src="${ static('desktop/js/ko.charts.js') }"></script>
  38. <script src="${ static('desktop/ext/js/knockout-sortable.min.js') }"></script>
  39. <script src="${ static('desktop/js/ko.editable.js') }"></script>
  40. <script src="${ static('desktop/ext/js/bootstrap-editable.min.js') }"></script>
  41. <script src="${ static('desktop/ext/js/wysihtml5-0.3.0.min.js') }"></script>
  42. <script src="${ static('desktop/js/bootstrap-wysihtml5-0.0.2.js') }"></script>
  43. <script src="${ static('desktop/ext/js/bootstrap-editable.wysihtml5.js') }"></script>
  44. <script src="${ static('beeswax/js/stats.utils.js') }"></script>
  45. ${ assist.assistJSModels() }
  46. <link rel="stylesheet" href="${ static('desktop/ext/css/bootstrap-editable.css') }">
  47. <link rel="stylesheet" href="${ static('desktop/ext/css/bootstrap-wysihtml5-0.0.2.css') }">
  48. <link rel="stylesheet" href="${ static('notebook/css/notebook.css') }">
  49. <link rel="stylesheet" href="${ static('notebook/css/notebook-layout.css') }">
  50. <style type="text/css">
  51. % if conf.CUSTOM.BANNER_TOP_HTML.get():
  52. .show-assist {
  53. top: 110px!important;
  54. }
  55. .main-content {
  56. top: 112px!important;
  57. }
  58. % endif
  59. </style>
  60. ${ assist.assistPanel() }
  61. % endif
  62. <span class="notebook">
  63. ${ components.menubar() }
  64. <script src="${ static('metastore/js/metastore.ko.js') }"></script>
  65. <link rel="stylesheet" href="${ static('metastore/css/metastore.css') }" type="text/css">
  66. <script type="text/html" id="metastore-breadcrumbs">
  67. <ul class="nav nav-pills hue-breadcrumbs-bar" id="breadcrumbs">
  68. <li>
  69. <a href="javascript:void(0);" data-bind="click: databasesBreadcrumb">${_('Databases')}
  70. <!-- ko if: database -->
  71. <span class="divider">&gt;</span>
  72. <!-- /ko -->
  73. </a>
  74. </li>
  75. <!-- ko with: database -->
  76. <li>
  77. <a href="javascript:void(0);" data-bind="click: $root.tablesBreadcrumb">
  78. <span data-bind="text: name"></span>
  79. <!-- ko with: table -->
  80. <span class="divider">&gt;</span>
  81. <!-- /ko -->
  82. </a>
  83. </li>
  84. <!-- ko with: table -->
  85. <li class="editable-breadcrumbs" title="${_('Edit path')}" data-bind="click: function(){ $parent.editingTable(true); }, visible: !$parent.editingTable()">
  86. <a href="javascript:void(0)" data-bind="text: name"></a>
  87. </li>
  88. <!-- /ko -->
  89. <!-- ko if: editingTable -->
  90. <!-- ko with: table -->
  91. <li class="editable-breadcrumb-input">
  92. <input type="text" data-bind="hivechooser: {data: name, database: $parent.name, skipColumns: true, searchEverywhere: true, onChange: function(val){ $parent.setTableByName(val); $parent.editingTable(false); }, apiHelperUser: '${ user }', apiHelperType: 'hive'}" autocomplete="off" />
  93. </li>
  94. <!-- /ko -->
  95. <!-- /ko -->
  96. <!-- /ko -->
  97. </ul>
  98. </script>
  99. <script type="text/html" id="metastore-columns-table">
  100. <div style="overflow-x: auto; overflow-y: hidden">
  101. <table class="table table-condensed table-nowrap">
  102. <br/>
  103. <thead>
  104. <tr>
  105. <th width="2%">&nbsp;</th>
  106. <th width="2%" class="no-sort">&nbsp;</th>
  107. <th width="17%">${_('Name')}</th>
  108. <th width="15%">${_('Type')}</th>
  109. <!-- ko if: $root.optimizerEnabled -->
  110. <th width="15%">${_('Popularity')}</th>
  111. <!-- /ko -->
  112. <th width="50%">${_('Comment')}</th>
  113. </tr>
  114. </thead>
  115. <tbody data-bind="hueach: {data: $data, itemHeight: 32, scrollable: '${ MAIN_SCROLLABLE }', scrollableOffset: 200, disableHueEachRowCount: 5, scrollUp: true}">
  116. <tr>
  117. <td data-bind="text: $index() + $indexOffset() + 1"></td>
  118. <td><a class="blue" href="javascript:void(0)" data-bind="click: showContextPopover"><i class="fa fa-fw fa-info" title="${_('Show details')}"></i></a></td>
  119. <td title="${ _("Scroll to the column") }">
  120. <!-- ko if: $root.database().table().samples.loading() -->
  121. <span data-bind="text: name"></span>
  122. <!-- ko if: typeof primary_key !== 'undefined' && primary_key() == 'true' -->
  123. <i class="fa fa-key"></i>
  124. <!-- /ko -->
  125. <!-- /ko -->
  126. <!-- ko ifnot: $root.database().table().samples.loading() -->
  127. <a href="javascript:void(0)" class="column-selector" data-bind="text: name, click: scrollToColumn"></a>
  128. <!-- /ko -->
  129. </td>
  130. <td data-bind="text: type"></td>
  131. <!-- ko if: $root.optimizerEnabled -->
  132. <td>
  133. <div class="progress" style="height: 10px; width: 70px; margin-top:5px;" data-bind="attr: { 'title': popularity() }">
  134. <div class="bar" style="background-color: #0B7FAD" data-bind="style: { 'width' : popularity() + '%' }"></div>
  135. </div>
  136. </td>
  137. <!-- /ko -->
  138. <td>
  139. % if has_write_access:
  140. <div class="show-inactive-on-hover">
  141. <a class="inactive-action pointer toggle-editable" title="${ _('Edit the comment') }"><i class="fa fa-pencil"></i></a>
  142. <span data-bind="editable: comment, editableOptions: {enabled: true, type: 'wysihtml5', toggle: 'manual', skipNewLines: true, toggleElement: '.toggle-editable', placement: 'left', placeholder: '${ _ko('Add a comment...') }', emptytext: '${ _ko('Add a comment...') }', inputclass: 'input-xlarge'}">
  143. ${ _('Add a comment...') }</span>
  144. </div>
  145. % else:
  146. <span data-bind="text: comment"></span>
  147. % endif
  148. </td>
  149. </tr>
  150. </tbody>
  151. </table>
  152. </div>
  153. </script>
  154. <script type="text/html" id="metastore-partition-columns-table">
  155. <div style="overflow-x: auto; overflow-y: hidden">
  156. <table class="table table-condensed table-nowrap">
  157. <thead>
  158. <tr>
  159. <th style="width: 1%">&nbsp;</th>
  160. <th>${_('Name')}</th>
  161. <th>${_('Type')}</th>
  162. </tr>
  163. </thead>
  164. <tbody>
  165. <!-- ko foreach: detailedKeys -->
  166. <tr>
  167. <td data-bind="text: $index() + 1"></td>
  168. <td data-bind="text: $data.name"></td>
  169. <td data-bind="text: $data.type"></td>
  170. </tr>
  171. <!-- /ko -->
  172. </tbody>
  173. </table>
  174. </div>
  175. </script>
  176. <script type="text/html" id="metastore-partition-values-table">
  177. <div style="overflow-x: auto; overflow-y: hidden">
  178. <table class="table table-condensed table-nowrap">
  179. <thead>
  180. <tr>
  181. <th style="width: 1%">&nbsp;</th>
  182. <th>${_('Values')}</th>
  183. <th>${_('Spec')}</th>
  184. <th>${_('Browse')}</th>
  185. </tr>
  186. </thead>
  187. <tbody>
  188. <!-- ko foreach: values -->
  189. <tr>
  190. <td data-bind="text: $index() + 1"></td>
  191. <td title="${_('Query partition data')}">
  192. <!-- ko if: IS_HUE_4 -->
  193. <a data-bind="click: function() { queryAndWatch(notebookUrl); }, text: '[\'' + columns.join('\',\'') + '\']'" href="javascript:void(0)"></a>
  194. <!-- /ko -->
  195. <!-- ko if: ! IS_HUE_4 -->
  196. <a data-bind="attr: {'href': readUrl }, text: '[\'' + columns.join('\',\'') + '\']'"></a>
  197. <!-- /ko -->
  198. </td>
  199. <td data-bind="text: partitionSpec"></td>
  200. <td>
  201. <!-- ko if: IS_HUE_4 -->
  202. <a data-bind="click: function () { browsePartitionFolder(browseUrl); }" href="javascript:void(0)" title="${_('Browse partition files')}">
  203. ${_('Files')}
  204. </a>
  205. <!-- /ko -->
  206. <!-- ko if: ! IS_HUE_4 -->
  207. <a data-bind="attr: {'href': browseUrl }" title="${_('Browse partition files')}">${_('Files')}</a>
  208. <!-- /ko -->
  209. </td>
  210. </tr>
  211. <!-- /ko -->
  212. </tbody>
  213. </table>
  214. </div>
  215. </script>
  216. <script type="text/html" id="metastore-samples-table">
  217. <table class="table table-condensed table-nowrap sample-table">
  218. <thead>
  219. <tr>
  220. <th style="width: 1%">&nbsp;</th>
  221. <!-- ko foreach: headers -->
  222. <th data-bind="text: $data"></th>
  223. <!-- /ko -->
  224. </tr>
  225. </thead>
  226. <tbody>
  227. <!-- ko foreach: rows -->
  228. <tr>
  229. <td data-bind="text: $index() + 1"></td>
  230. <!-- ko foreach: $data -->
  231. <td data-bind="text: $data"></td>
  232. <!-- /ko -->
  233. </tr>
  234. <!-- /ko -->
  235. </tbody>
  236. </table>
  237. <div id="jumpToColumnAlert" class="alert hide" style="margin-top: 12px;">
  238. <button type="button" class="close" data-dismiss="alert">&times;</button>
  239. <strong>${_('Did you know?')}</strong>
  240. <ul>
  241. <li>${ _('If the sample contains a large number of columns, click a row to select a column to jump to') }</li>
  242. </ul>
  243. </div>
  244. </script>
  245. <script type="text/html" id="metastore-table-properties">
  246. <i data-bind="visible: loadingDetails" class="fa fa-spinner fa-spin fa-2x muted" style="display: none;"></i>
  247. <!-- ko with: tableDetails -->
  248. <h4>${ _('Properties') }</h4>
  249. <div class="row-fluid">
  250. <div title="${ _('Type') }">
  251. <!-- ko if: is_view -->
  252. <i class="fa fa-fw fa-eye muted"></i> ${ _('View') }
  253. <!-- /ko -->
  254. <!-- ko ifnot: is_view -->
  255. <i class="fa fa-fw fa-table muted"></i> ${ _('Table') }
  256. <!-- /ko -->
  257. </div>
  258. <div title="${ _('Owner') }">
  259. <i class="fa fa-fw fa-user muted"></i> <span data-bind="text: details.properties.owner"></span>
  260. </div>
  261. <div title="${ _('Created') }"><i class="fa fa-fw fa-clock-o muted"></i> <span data-bind="text: localeFormat(details.properties.create_time)"></span></div>
  262. <div title="${ _('Format') }">
  263. <i class="fa fa-fw fa-file-o muted"></i> <span data-bind="text: details.properties.format"></span>
  264. <i class="fa fa-fw fa-archive muted"></i>
  265. <span data-bind="visible: details.properties.compressed" style="display:none;">${_('Compressed')}</span>
  266. <span data-bind="visible: ! details.properties.compressed" style="display:none;">${_('Not compressed')}</span>
  267. </div>
  268. </div>
  269. <!-- /ko -->
  270. </script>
  271. <script type="text/html" id="metastore-table-stats">
  272. <!-- ko if: tableDetails() && ! tableDetails().is_view -->
  273. <!-- ko with: tableDetails -->
  274. <h4>${ _('Stats') }
  275. <!-- ko ifnot: partition_keys.length -->
  276. % if has_write_access:
  277. <!-- ko if: $parent.refreshingTableStats -->
  278. <i class="fa fa-refresh fa-spin"></i>
  279. <!-- /ko -->
  280. <!-- ko ifnot: $parent.refreshingTableStats() || is_view -->
  281. <a class="pointer" href="javascript: void(0);" data-bind="click: $parent.refreshTableStats"><i class="fa fa-refresh"></i></a>
  282. <!-- /ko -->
  283. % endif
  284. <span data-bind="visible: details.stats.COLUMN_STATS_ACCURATE == 'false' && ! is_view" rel="tooltip" data-placement="top" title="${ _('The column stats for this table are not accurate') }"><i class="fa fa-exclamation-triangle"></i></span>
  285. <!-- /ko -->
  286. </h4>
  287. <div class="row-fluid">
  288. <div>
  289. <i class="fa fa-fw fa-hdd-o muted"></i>
  290. <!-- ko if: details.properties.format == 'kudu' -->
  291. ${_('Stored in')} Kudu
  292. <!-- /ko -->
  293. <!-- ko if: details.properties.format != 'kudu' -->
  294. <a data-bind="attr: {'href': hdfs_link, 'rel': path_location}" title="${_('Open data location')}">${_('Location')}</a>
  295. <!-- /ko -->
  296. </div>
  297. <!-- ko with: $parent.tableStats -->
  298. <!-- ko if: typeof last_modified_by !== 'undefined' -->
  299. <div title="${ _('Last modified by') }"><i class="fa fa-fw fa-user muted"></i> <span data-bind="text: last_modified_by"></span> </div>
  300. <!-- /ko -->
  301. <!-- ko if: typeof last_modified_time !== 'undefined' -->
  302. <div title="${ _('Last modified time') }"><i class="fa fa-fw fa-clock-o muted"></i> <span data-bind="text: localeFormat(last_modified_time*1000)"></span></div>
  303. <!-- /ko -->
  304. <!-- ko if: typeof numFiles !== 'undefined' && typeof last_modified_by === 'undefined' -->
  305. <div title="${ _('Number of files') }"><i class="fa fa-fw fa-files-o muted"></i> <span data-bind="text: numFiles"></span> ${ _('files') }</div>
  306. <!-- /ko -->
  307. <!-- ko if: typeof numRows !== 'undefined' -->
  308. <div title="${ _('Number of rows') }"><i class="fa fa-fw fa-list muted"></i> <span data-bind="text: numRows"></span> ${ _('rows') }</div>
  309. <!-- /ko -->
  310. <!-- ko if: typeof totalSize !== 'undefined' && typeof last_modified_by === 'undefined' -->
  311. <div title="${ _('Total size') }"><i class="fa fa-fw fa-tasks muted"></i> <span data-bind="text: filesize(totalSize)"></span></div>
  312. <!-- /ko -->
  313. <!-- /ko -->
  314. </div>
  315. <!-- /ko -->
  316. <!-- /ko -->
  317. </script>
  318. <script type="text/html" id="metastore-databases">
  319. <div class="actionbar-actions" data-bind="dockable: { scrollable: '${ MAIN_SCROLLABLE }', nicescroll: true, jumpCorrection: 5 }">
  320. <input class="input-xlarge search-query margin-left-10" type="text" placeholder="${ _('Search for a database...') }" data-bind="clearable: databaseQuery, value: databaseQuery, valueUpdate: 'afterkeydown'"/>
  321. % if has_write_access:
  322. <button class="btn toolbarBtn margin-left-20" title="${_('Drop the selected databases')}" data-bind="click: function () { $('#dropDatabase').modal('show'); }, disable: selectedDatabases().length === 0"><i class="fa fa-times"></i> ${_('Drop')}</button>
  323. <div id="dropDatabase" class="modal hide fade">
  324. % if is_embeddable:
  325. <form action="/metastore/databases/drop" data-bind="submit: dropAndWatch" method="POST">
  326. <input type="hidden" name="is_embeddable" value="true"/>
  327. % else:
  328. <form id="dropDatabaseForm" action="/metastore/databases/drop" method="POST">
  329. % endif
  330. ${ csrf_token(request) | n,unicode }
  331. <div class="modal-header">
  332. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  333. <h2 id="dropDatabaseMessage" class="modal-title">${ _('Do you really want to delete the following database(s)?') }</h2>
  334. </div>
  335. <div class="modal-body">
  336. <ul data-bind="foreach: selectedDatabases">
  337. <li>
  338. <span data-bind="text: name"></span>
  339. <!-- ko if: $data.tables().length > 0 -->
  340. (<span data-bind="text: $data.tables().length"></span> tables)
  341. <!-- /ko -->
  342. </li>
  343. </ul>
  344. </div>
  345. <div class="modal-footer">
  346. <div class="label label-important margin-top-5 pull-left">${ _('Warning: This will drop all tables and objects within the database.') }</div>
  347. <input type="button" class="btn" data-dismiss="modal" value="${_('No')}">
  348. <input type="submit" class="btn btn-danger" value="${_('Yes')}"/>
  349. </div>
  350. <!-- ko foreach: selectedDatabases -->
  351. <input type="hidden" name="database_selection" data-bind="value: name" />
  352. <!-- /ko -->
  353. </form>
  354. </div>
  355. % endif
  356. </div>
  357. <table id="databasesTable" class="table table-condensed datatables" style="margin-bottom: 10px" data-bind="visible: filteredDatabases().length > 0">
  358. <thead>
  359. <tr>
  360. <th width="1%" style="text-align: center"><div class="hueCheckbox fa" data-bind="hueCheckAll: { allValues: filteredDatabases, selectedValues: selectedDatabases }"></div></th>
  361. <th>${ _('Database Name') }</th>
  362. </tr>
  363. </thead>
  364. <tbody data-bind="hueach: {data: filteredDatabases, itemHeight: 32, scrollable: '${ MAIN_SCROLLABLE }', scrollableOffset: 145, scrollUp: true}">
  365. <tr>
  366. <td width="1%" style="text-align: center">
  367. <div class="hueCheckbox fa" data-bind="multiCheck: '#databasesTable', value: $data, hueChecked: $parent.selectedDatabases"></div>
  368. </td>
  369. <td>
  370. <a href="javascript: void(0);" data-bind="text: name, click: function () { $parent.setDatabase($data, function(){ huePubSub.publish('metastore.url.change'); }) }"></a>
  371. </td>
  372. </tr>
  373. </tbody>
  374. </table>
  375. <span class="margin-left-10" data-bind="visible: filteredDatabases().length === 0" style="font-style: italic; display: none;">${_('No databases found')}</span>
  376. </script>
  377. <script type="text/html" id="metastore-tables">
  378. <div class="row-fluid">
  379. <!-- ko with: stats -->
  380. <div class="
  381. %if is_optimizer_enabled:
  382. span8
  383. %else:
  384. span12
  385. %endif
  386. tile">
  387. <div class="span6 tile">
  388. <h4>${ _('Properties') }</h4>
  389. <div title="${ _('Comment') }"><i class="fa fa-fw fa-comment muted"></i>
  390. <!-- ko if: comment -->
  391. <span data-bind="text: comment"></span>
  392. <!-- /ko -->
  393. <!-- ko ifnot: comment -->
  394. <i>${_('No comment.')}</i>
  395. <!-- /ko -->
  396. <div title="${ _('Owner') }">
  397. <i class="fa fa-fw fa-user muted"></i>
  398. <span data-bind="text: owner_name"></span> (<span data-bind="text: owner_type"></span>)
  399. </br>
  400. <i class="fa fa-fw fa-hdd-o muted"></i> <a data-bind="attr: {'href': hdfs_link, 'rel': location }"> ${_('Location')}</a>
  401. </div>
  402. </div>
  403. </div>
  404. <div class="span6 tile">
  405. <!-- ko if: $root.navigatorEnabled() -->
  406. <h4>${ _('Tags') }</h4>
  407. <div style="margin-top: 5px" data-bind="component: { name: 'nav-tags', params: {
  408. sourceType: 'hive',
  409. database: db_name
  410. } }"></div>
  411. <!-- /ko -->
  412. </div>
  413. <!-- ko with: parameters -->
  414. <div class="row-fluid">
  415. <div class="span12">
  416. <div title="${ _('Parameters') }">
  417. <!-- ko template: { name: 'metastore-databases-parameters', data: hueUtils.parseHivePseudoJson($data) }--><!-- /ko -->
  418. </div>
  419. </div>
  420. </div>
  421. <!-- /ko -->
  422. </div>
  423. <!-- /ko -->
  424. </div>
  425. <div class="row-fluid">
  426. <div class="span12 tile">
  427. <h4>${ _('Tables') }</h4>
  428. <div class="actionbar-actions" data-bind="visible: tables().length > 0, dockable: { scrollable: '${ MAIN_SCROLLABLE }', nicescroll: true, jumpCorrection: 5 }">
  429. <input class="input-xlarge search-query margin-left-10" type="text" placeholder="${ _('Search for a table...') }" data-bind="clearable: tableQuery, value: tableQuery, valueUpdate: 'afterkeydown'"/>
  430. <button class="btn toolbarBtn margin-left-20" title="${_('Browse the selected table')}" data-bind="click: function () { setTable(selectedTables()[0]); selectedTables([]); }, disable: selectedTables().length !== 1"><i class="fa fa-eye"></i> ${_('View')}</button>
  431. <button class="btn toolbarBtn" title="${_('Query the selected table')}" data-bind="click: function () { IS_HUE_4 ? queryAndWatch('/notebook/browse/' + name + '/' + selectedTables()[0].name + '/') : location.href = '/notebook/browse/' + name + '/' + selectedTables()[0].name; }, disable: selectedTables().length !== 1">
  432. <i class="fa fa-play fa-fw"></i> ${_('Query')}
  433. </button>
  434. % if has_write_access:
  435. <button id="dropBtn" class="btn toolbarBtn" title="${_('Delete the selected tables')}" data-bind="click: function () { $('#dropTable').modal('show'); }, disable: selectedTables().length === 0"><i class="fa fa-times"></i> ${_('Drop')}</button>
  436. % endif
  437. <!-- ko if: $root.optimizerEnabled -->
  438. &nbsp;
  439. &nbsp;
  440. <button class="btn toolbarBtn" title="${_('View the selected table')}" data-bind="click: function () { window.open($root.optimizerUrl() + '#/table/' + selectedTables()[0].optimizerStats().eid, '_blank'); }, disable: selectedTables().length !== 1">
  441. <i class="fa fa-skyatlas"></i> ${_('View in Optimizer')}
  442. </button>
  443. <!-- /ko -->
  444. </div>
  445. <table id="tablesTable" class="table table-condensed table-nowrap" style="margin-bottom: 10px; width: 100%" data-bind="visible: filteredTables().length > 0">
  446. <thead>
  447. <tr>
  448. <th width="1%" style="text-align: center"><div class="hueCheckbox fa" data-bind="hueCheckAll: { allValues: filteredTables, selectedValues: selectedTables }"></div></th>
  449. <th>&nbsp;</th>
  450. <th width="30%">${ _('Table Name') }</th>
  451. <th width="48%">${ _('Comment') }</th>
  452. <!-- ko if: $root.optimizerEnabled -->
  453. <th width="10%">${ _('Popularity') }</th>
  454. <th width="10%">${ _('Columns') }</th>
  455. <!-- /ko -->
  456. <th width="1%">${ _('Type') }</th>
  457. </tr>
  458. </thead>
  459. <tbody data-bind="hueach: {data: filteredTables, itemHeight: 32, scrollable: '${ MAIN_SCROLLABLE }', scrollableOffset: 277, scrollUp: true}">
  460. <tr>
  461. <td width="1%" style="text-align: center">
  462. <div class="hueCheckbox fa" data-bind="multiCheck: '#tablesTable', value: $data, hueChecked: $parent.selectedTables"></div>
  463. </td>
  464. <td width="1%"><a class="blue" href="javascript:void(0)" data-bind="click: showContextPopover"><i class="fa fa-fw fa-info" title="${_('Show details')}"></i></a></td>
  465. <td>
  466. <a class="tableLink" href="javascript:void(0);" data-bind="text: name, click: function() { $parent.setTable($data, function(){ huePubSub.publish('metastore.url.change'); }) }"></a>
  467. </td>
  468. <td style="text-overflow: ellipsis; overflow: hidden; max-width: 0" data-bind="html: commentWithoutNewLines, attr: {title: hueUtils.html2text(commentWithoutNewLines())}"></td>
  469. <!-- ko if: $root.optimizerEnabled -->
  470. <!-- ko if: optimizerStats() -->
  471. <td>
  472. <div class="progress" style="height: 10px; width: 70px; margin-top:5px;" data-bind="attr: {'title': optimizerStats().popularity}">
  473. <div class="bar" style="background-color: #0B7FAD" data-bind="style: { 'width' : optimizerStats().popularity + '%' }"></div>
  474. </div>
  475. </td>
  476. <td data-bind="text: optimizerStats().column_count"></td>
  477. <!-- /ko -->
  478. <!-- ko ifnot: optimizerStats() -->
  479. <td></td>
  480. <td></td>
  481. <!-- /ko -->
  482. <!-- /ko -->
  483. <td class="center">
  484. <!-- ko ifnot: $root.optimizerEnabled && optimizerStats() -->
  485. <!-- ko if: type == 'Table' -->
  486. <i class="fa fa-fw fa-table muted" title="${ _('Table') }"></i>
  487. <!-- /ko -->
  488. <!-- ko if: type == 'View' -->
  489. <i class="fa fa-fw fa-eye muted" title="${ _('View') }"></i>
  490. <!-- /ko -->
  491. <!-- /ko -->
  492. <!-- ko if: $root.optimizerEnabled && optimizerStats() -->
  493. <!-- ko if: optimizerStats().is_fact -->
  494. <i class="fa fa-fw muted fa-database" title="${ _('Fact table') }"></i>
  495. <!-- /ko -->
  496. <!-- ko ifnot: optimizerStats().is_fact -->
  497. <i class="fa fa-fw muted fa-calendar" title="${ _('Dimension table') }"></i>
  498. <!-- /ko -->
  499. <!-- /ko -->
  500. </td>
  501. </tr>
  502. </tbody>
  503. </table>
  504. <span data-bind="visible: filteredTables().length === 0, css: {'margin-left-10': tables().length > 0}" style="font-style: italic; display: none;">${_('No tables found.')}</span>
  505. </div>
  506. </div>
  507. % if has_write_access:
  508. <div id="dropTable" class="modal hide fade">
  509. % if is_embeddable:
  510. <form data-bind="attr: { 'action': '/metastore/tables/drop/' + name }, submit: dropAndWatch" method="POST">
  511. <input type="hidden" name="is_embeddable" value="true"/>
  512. % else:
  513. <form data-bind="attr: { 'action': '/metastore/tables/drop/' + name }" method="POST">
  514. % endif
  515. ${ csrf_token(request) | n,unicode }
  516. <div class="modal-header">
  517. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  518. <h2 id="dropTableMessage" class="modal-title">${_('Do you really want to drop the selected table(s)?')}</h2>
  519. </div>
  520. <div class="modal-body">
  521. <ul data-bind="foreach: selectedTables">
  522. <!-- ko if: $index() <= 9 -->
  523. <li>
  524. <span data-bind="text: name"></span>
  525. </li>
  526. <!-- /ko -->
  527. </ul>
  528. <!-- ko if: selectedTables().length > 10 -->
  529. ${_('and')} <span data-bind="text: selectedTables().length - 10"></span> ${_('others')}.<br>
  530. <!-- /ko -->
  531. <label class="checkbox" style="display: inline-block; margin-top: 5px">
  532. <input type="checkbox" name="skip_trash" /> ${ _('Skip the trash') }
  533. </label>
  534. </div>
  535. <div class="modal-footer">
  536. <input type="button" class="btn" data-dismiss="modal" value="${_('No')}" />
  537. <input type="submit" class="btn btn-danger" value="${_('Yes')}"/>
  538. </div>
  539. <!-- ko foreach: selectedTables -->
  540. <input type="hidden" name="table_selection" data-bind="value: name" />
  541. <!-- /ko -->
  542. </form>
  543. </div>
  544. % endif
  545. </script>
  546. <script type="text/html" id="metastore-databases-parameters">
  547. <div data-bind="toggleOverflow: {height: 24}">
  548. <div class="inline margin-right-20"><i class="fa fa-fw fa-cog muted"></i></div>
  549. <!-- ko foreach: Object.keys($data) -->
  550. <div class="inline margin-right-20"><strong data-bind="text: $data"></strong>: <span data-bind="text: $parent[$data]"></span></div>
  551. <!-- /ko -->
  552. </div>
  553. </script>
  554. <script type="text/html" id="metastore-databases-actions">
  555. <div class="inline-block pull-right">
  556. <a class="inactive-action" href="javascript:void(0)" data-bind="tooltip: { placement: 'bottom', delay: 750 }, click: function () { huePubSub.publish('assist.db.refresh', { sourceType: 'hive' }); }"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : $root.reloading }" title="${_('Refresh')}"></i></a>
  557. % if has_write_access:
  558. % if is_embeddable:
  559. <a class="inactive-action margin-left-10" data-bind="tooltip: { placement: 'bottom', delay: 750 }, click: function () { huePubSub.publish('open.link', '${ url('indexer:importer_prefill', source_type='manual', target_type='database') }'); }" title="${_('Create a new database')}" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
  560. % elif ENABLE_NEW_CREATE_TABLE.get():
  561. <a class="inactive-action margin-left-10" data-bind="tooltip: { placement: 'bottom', delay: 750 }, attr: { 'href': '${ url('indexer:importer_prefill', source_type='manual', target_type='database') }' }" title="${_('Create a new database')}"><i class="fa fa-plus"></i></a>
  562. % else:
  563. <a class="inactive-action margin-left-10" data-bind="tooltip: { placement: 'bottom', delay: 750 }" href="${ url('beeswax:create_database') }" title="${_('Create a new database')}"><i class="fa fa-plus"></i></a>
  564. % endif
  565. % endif
  566. </div>
  567. </script>
  568. <script type="text/html" id="metastore-tables-actions">
  569. <div class="inline-block pull-right">
  570. <a class="inactive-action" href="javascript:void(0)" data-bind="tooltip: { placement: 'bottom', delay: 750 }, click: function () { huePubSub.publish('assist.db.refresh', { sourceType: 'hive' }); }" title="${_('Refresh')}"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : $root.reloading }"></i></a>
  571. % if has_write_access:
  572. % if is_embeddable:
  573. <a class="inactive-action margin-left-10" data-bind="tooltip: { placement: 'bottom', delay: 750 }, click: function () { huePubSub.publish('open.link', '${ url('indexer:importer_prefill', source_type='all', target_type='table') }' + database().name ); }" title="${_('Create a new table')}" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
  574. % elif ENABLE_NEW_CREATE_TABLE.get():
  575. <a class="inactive-action margin-left-10" data-bind="tooltip: { placement: 'bottom', delay: 750 }, attr: { 'href': '${ url('indexer:importer_prefill', source_type='all', target_type='table') }' + database().name }" title="${_('Create a new table')}"><i class="fa fa-plus"></i></a>
  576. % else:
  577. <a class="inactive-action margin-left-10" data-bind="tooltip: { placement: 'bottom', delay: 750 }, attr: { 'href': '/beeswax/create/import_wizard/' + database().name }" title="${_('Create a new table from a file')}"><span class="fa-stack fa-fw" style="width: 1.28571429em"><i class="fa fa-file-o fa-stack-1x"></i><i class="fa fa-plus-circle fa-stack-1x" style="font-size: 14px; margin-left: 5px; margin-top: 6px;"></i></span></a>
  578. <a class="inactive-action margin-left-10" data-bind="tooltip: { placement: 'bottom', delay: 750 }, attr: { 'href': '/beeswax/create/create_table/' + database().name }" title="${_('Create a new table manually')}"><i class="fa fa-plus"></i></a>
  579. % endif
  580. % endif
  581. </div>
  582. </script>
  583. <script type="text/html" id="metastore-describe-table-actions">
  584. <div class="inline-block pull-right">
  585. <!-- ko with: database -->
  586. <!-- ko with: table -->
  587. % if USE_NEW_EDITOR.get():
  588. <!-- ko if: IS_HUE_4 -->
  589. <a class="inactive-action" data-bind="tooltip: { placement: 'bottom', delay: 750 }, click: function() { queryAndWatch('/notebook/browse/' + database.name + '/' + name + '/'); }" title="${_('Query the table')}" href="javascript:void(0)"><i class="fa fa-play fa-fw"></i></a>
  590. <!-- /ko -->
  591. <!-- ko if: ! IS_HUE_4 -->
  592. <a class="inactive-action" data-bind="tooltip: { placement: 'bottom', delay: 750 }, attr: { 'href': '/notebook/browse/' + database.name + '/' + name }" title="${_('Query the table')}"><i class="fa fa-play fa-fw"></i></a>
  593. <!-- /ko -->
  594. % else:
  595. <a class="inactive-action" data-bind="tooltip: { placement: 'bottom', delay: 750 }, attr: { 'href': '/metastore/table/'+ database.name + '/' + name + '/read' }" title="${_('Browse Data')}"><i class="fa fa-play fa-fw"></i></a>
  596. % endif
  597. <a class="inactive-action" data-bind="tooltip: { placement: 'bottom', delay: 750 }, click: function () { huePubSub.publish('assist.db.refresh', { sourceType: 'hive' }); }" title="${_('Refresh')}" href="javascript:void(0)"><i class="pointer fa fa-refresh fa-fw" data-bind="css: { 'fa-spin blue' : $root.reloading }"></i></a>
  598. % if user.is_superuser:
  599. <!-- ko if: $root.optimizerEnabled() && $root.database().table().optimizerStats() -->
  600. <a class="inactive-action" title="${_('View in Optimizer')}" data-bind="tooltip: { placement: 'bottom', delay: 750 }, attr: { 'href': $root.optimizerUrl() + '#/table/' + $root.database().table().optimizerStats().eid }" target="_blank"><i class="fa fa-skyatlas fa-fw"></i></a>
  601. <!-- /ko -->
  602. % endif
  603. % if has_write_access:
  604. <a class="inactive-action" href="#" data-bind="tooltip: { placement: 'bottom', delay: 750 }, click: showImportData, visible: tableDetails() && ! tableDetails().is_view" title="${_('Import Data')}"><i class="fa fa-upload fa-fw"></i></a>
  605. % endif
  606. <!-- ko if: tableDetails() -->
  607. <!-- ko if: tableDetails().partition_keys.length -->
  608. <a class="inactive-action" data-bind="tooltip: { placement: 'bottom', delay: 750 }, attr: { 'href': '/metastore/table/' + database.name + '/' + name + '/partitions' }" title="${_('Show Partitions')}"><i class="fa fa-sitemap fa-fw"></i></a>
  609. <!-- /ko -->
  610. <!-- /ko -->
  611. % if has_write_access:
  612. <a class="inactive-action" href="#dropSingleTable" data-toggle="modal" data-bind="tooltip: { placement: 'bottom', delay: 750 }, attr: { 'title' : tableDetails() && tableDetails().is_view ? '${_('Drop View')}' : '${_('Drop Table')}' }"><i class="fa fa-times fa-fw"></i></a>
  613. % endif
  614. <!-- /ko -->
  615. <!-- /ko -->
  616. </div>
  617. </script>
  618. <script type="text/html" id="metastore-overview-tab">
  619. <div class="row-fluid margin-top-10">
  620. <div class="span3 tile">
  621. <!-- ko template: 'metastore-table-properties' --><!-- /ko -->
  622. </div>
  623. <div class="span3 tile">
  624. <!-- ko template: 'metastore-table-stats' --><!-- /ko -->
  625. </div>
  626. <!-- ko if: $root.navigatorEnabled() && navigatorStats() -->
  627. <div class="span6 tile">
  628. <h4>${ _('Tags') }</h4>
  629. <div style="margin-top: 5px" data-bind="component: { name: 'nav-tags', params: {
  630. sourceType: 'hive',
  631. database: database.name,
  632. table: name
  633. } }"></div>
  634. </div>
  635. <!-- /ko -->
  636. </div>
  637. <div class="tile">
  638. <h4>${ _('Columns') } (<span data-bind="text: columns().length"></span>) <i data-bind="visible: loadingColumns" class="fa fa-spinner fa-spin" style="display: none;"></i></h4>
  639. <!-- ko with: favouriteColumns -->
  640. <!-- ko template: "metastore-columns-table" --><!-- /ko -->
  641. <!-- /ko -->
  642. <a class="pointer" data-bind="visible: columns().length > favouriteColumns().length, click: function() { $('li a[href=\'#columns\']').click(); }">
  643. ${_('View more...')}
  644. </a>
  645. </div>
  646. <div class="tile">
  647. <h4>${ _('Sample') } <i data-bind="visible: samples.loading" class='fa fa-spinner fa-spin' style="display: none;"></i></h4>
  648. <!-- ko with: samples -->
  649. <!-- ko if: loaded() && !hasErrors() -->
  650. <!-- ko with: preview -->
  651. <!-- ko template: { if: rows().length, name: 'metastore-samples-table' } --><!-- /ko -->
  652. <a class="pointer" data-bind="visible: rows().length >= 3, click: function() { $('li a[href=\'#sample\']').click(); }" style="display: none;">
  653. ${_('View more...')}
  654. </a>
  655. <!-- /ko -->
  656. <div data-bind="visible: !rows().length && metastoreTable.tableDetails().is_view" style="display: none;">${ _('The view does not contain any data.') }</div>
  657. <div data-bind="visible: !rows().length && !metastoreTable.tableDetails().is_view" style="display: none;">${ _('The table does not contain any data.') }</div>
  658. <!-- /ko -->
  659. <!-- ko if: hasErrors() -->
  660. <div>${ _('Could not load the sample, see the server log for details.') }</div>
  661. <!-- /ko -->
  662. <!-- /ko -->
  663. </div>
  664. <div class="tile" data-bind="visible: tableDetails() && tableDetails().partition_keys.length" style="display: none;">
  665. <!-- ko with: partitions -->
  666. <h4>${ _('Partitions') } <i data-bind="visible: loading" class='fa fa-spinner fa-spin' style="display: none;"></i></h4>
  667. <!-- ko if: loaded -->
  668. <!-- ko with: preview -->
  669. <!-- ko template: { if: values().length, name: 'metastore-partition-values-table' } --><!-- /ko -->
  670. <a class="pointer" data-bind="visible: values().length >= 3, click: function() { $('li a[href=\'#partitions\']').click(); }" style="display: none;">
  671. ${_('View more...')}
  672. </a>
  673. <!-- /ko -->
  674. <span data-bind="visible: !values().length" style="display: none;">${ _('The partition does not contain any values') }</span>
  675. <!-- /ko -->
  676. <!-- /ko -->
  677. </div>
  678. </script>
  679. <script type="text/html" id="metastore-columns-tab">
  680. <!-- ko with: filteredColumns -->
  681. <!-- ko template: "metastore-columns-table" --><!-- /ko -->
  682. <!-- /ko -->
  683. </script>
  684. <script type="text/html" id="metastore-partitions-tab">
  685. <!-- ko with: partitions -->
  686. <div class="tile" data-bind="visible: true" style="display: none;">
  687. <h4>${ _('Columns') }</h4>
  688. <!-- ko template: 'metastore-partition-columns-table' --><!-- /ko -->
  689. </div>
  690. <div class="tile" data-bind="visible: true" style="display: none;">
  691. <h4>${ _('Partitions') } <i data-bind="visible: loading" class='fa fa-spinner fa-spin' style="display: none;"></i></h4>
  692. <!-- ko if: loaded -->
  693. <!-- ko template: { if: values().length, name: 'metastore-partition-values-table' } --><!-- /ko -->
  694. <span data-bind="visible: !values().length" style="display: none;">${ _('The partition does not contain any values') }</span>
  695. <!-- /ko -->
  696. </div>
  697. <!-- /ko -->
  698. <a data-bind="attr: { 'href': '/metastore/table/' + database.name + '/' + name + '/partitions' }">${ _('View all') }</a>
  699. </script>
  700. <script type="text/html" id="metastore-sample-tab">
  701. <!-- ko with: samples -->
  702. <!-- ko if: loading -->
  703. <div class="empty-message">
  704. <i data-bind="visible: loading" class='fa fa-spinner fa-spin' style="display: none;"></i>
  705. </div>
  706. <!-- /ko -->
  707. <!-- ko if: loaded() && !hasErrors() -->
  708. <!-- ko template: { if: rows().length, name: 'metastore-samples-table' } --><!-- /ko -->
  709. <div data-bind="visible: !rows().length && metastoreTable.tableDetails().is_view" style="display: none;" class="empty-message">${ _('The view does not contain any data.') }</div>
  710. <div data-bind="visible: !rows().length && !metastoreTable.tableDetails().is_view" style="display: none;" class="empty-message">${ _('The table does not contain any data.') }</div>
  711. <!-- /ko -->
  712. <!-- ko if: hasErrors() -->
  713. <div class="empty-message">${ _('Could not load the sample, see the server log for details.') }</div>
  714. <!-- /ko -->
  715. <!-- /ko -->
  716. </script>
  717. <script type="text/html" id="metastore-queries-tab">
  718. <br/>
  719. <i class="fa fa-spinner fa-spin" data-bind="visible: $root.loadingQueries"></i>
  720. <table data-bind="visible: ! loadingQueries() && $data.optimizerDetails().queryList().length > 0" class="table table-condensed">
  721. <thead>
  722. <tr>
  723. <th width="10%">${ _('Id') }</th>
  724. <th width="10%">${ _('Popularity') }</th>
  725. <th>${ _('Character') }</th>
  726. <th>${ _('Query') }</th>
  727. <th width="20%">${ _('Complexity') }</th>
  728. <th width="10%">${ _('Hive Compatible') }</th>
  729. <th width="10%">${ _('Impala Compatible') }</th>
  730. </tr>
  731. </thead>
  732. <tbody data-bind="hueach: {data: $data.optimizerDetails().queryList(), itemHeight: 32, scrollable: '${ MAIN_SCROLLABLE }', scrollableOffset: 200}">
  733. <tr class="pointer" data-bind="click: function(){ window.open($root.optimizerUrl() + '#/query/' + qid(), '_blank'); }">
  734. <td data-bind="text: qid"></td>
  735. <td style="height: 10px; width: 70px; margin-top:5px;" data-bind="attr: {'title': queryCount()}">
  736. <div class="progress bar" style="background-color: #0B7FAD" data-bind="style: { 'width' : Math.round(queryCount() / $parent.optimizerDetails().queryCount() * 100) + '%' }"></div>
  737. </td>
  738. <td><code data-bind="text: queryChar"></code></td>
  739. <td><code data-bind="text: query().substring(0, 100) + '...'"></code></td>
  740. <td data-bind="text: complexity, css: {'alert-success': complexity() == 'Low', 'alert-warning': complexity() == 'Medium', 'alert-danger': complexity() == 'High'}" class="alert"></td>
  741. <td data-bind="text: hiveCompatible"></td>
  742. <td data-bind="text: impalaCompatible"></td>
  743. </tr>
  744. </tbody>
  745. </table>
  746. <div data-bind="visible: ! loadingQueries() && $data.optimizerDetails().queryList().length == 0" class="empty-message">
  747. ${ _('No queries found for the current table.') }
  748. </div>
  749. </script>
  750. <script type="text/html" id="metastore-details-tab">
  751. <!-- ko with: tableDetails -->
  752. <table class="properties-table">
  753. <tbody data-bind="foreach: properties">
  754. <!-- ko if: col_name.indexOf('#') === 0 -->
  755. <tr>
  756. <td colspan="3"><h4 data-bind="text: col_name.substring(1)"></h4></td>
  757. </tr>
  758. <!-- /ko -->
  759. <!-- ko ifnot: col_name.indexOf('#') === 0 -->
  760. <tr>
  761. <td class="property-name" data-bind="text: col_name || '&nbsp;'"></td>
  762. <td data-bind="text: data_type || '&nbsp;'"></td>
  763. <td data-bind="text: comment || '&nbsp;'"></td>
  764. </tr>
  765. <!-- /ko -->
  766. </tbody>
  767. </table>
  768. <!-- /ko -->
  769. </script>
  770. <script type="text/html" id="metastore-describe-table">
  771. <div class="clearfix"></div>
  772. % if has_write_access:
  773. <div class="show-inactive-on-hover">
  774. <a class="inactive-action pointer toggle-editable" title="${ _('Edit the description') }"><i class="fa fa-pencil vertical-align-top"></i></a>
  775. <div data-bind="editable: comment, editableOptions: {enabled: true, type: 'wysihtml5', toggle: 'manual', toggleElement: '.toggle-editable', placement: 'bottom', placeholder: '${ _ko('Add a description...') }', emptytext: '${ _ko('No description available') }', inputclass:'input-xlarge', rows: 10 }" class="inline-block margin-left-5">
  776. ${ _('Add a description...') }
  777. </div>
  778. </div>
  779. % else:
  780. <span data-bind="html: comment"></span>
  781. %endif
  782. <ul class="nav nav-pills margin-top-30">
  783. <li><a href="#overview" data-toggle="tab" data-bind="click: function(){ $root.currentTab('table-overview'); }">${_('Overview')}</a></li>
  784. <li><a href="#columns" data-toggle="tab" data-bind="click: function(){ $root.currentTab('table-columns'); }">${_('Columns')} (<span data-bind="text: columns().length"></span>)</a></li>
  785. <!-- ko if: tableDetails() && tableDetails().partition_keys.length -->
  786. <li><a href="#partitions" data-toggle="tab" data-bind="click: function(){ $root.currentTab('table-partitions'); }">${_('Partitions')} (<span data-bind="text: partitionsCountLabel"></span>)</a></li>
  787. <!-- /ko -->
  788. <li><a href="#sample" data-toggle="tab" data-bind="click: function(){ $root.currentTab('table-sample'); }">${_('Sample')}</a></li>
  789. <!-- ko if: $root.optimizerEnabled() -->
  790. <!-- ko if: $root.database().table().optimizerDetails() -->
  791. ##<li><a href="#permissions" data-toggle="tab" data-bind="click: function(){ $root.currentTab('table-permissions'); }">${_('Permissions')}</a></li>
  792. ##<li><a href="#queries" data-toggle="tab" data-bind="click: function(){ $root.currentTab('table-queries'); }">${_('Queries')} (<span data-bind="text: $root.database().table().optimizerDetails().queryCount"></span>)</a></li>
  793. ##<li><a href="#joins" data-toggle="tab" data-bind="click: function(){ $root.currentTab('table-joins'); }">${_('Joins')} (<span data-bind="text: $root.database().table().optimizerDetails().joinCount"></span>)</a></li>
  794. <!-- /ko -->
  795. ##<!-- ko if: $root.database().table().relationshipsDetails() -->
  796. ##<li><a href="#relationships" data-toggle="tab" data-bind="click: function(){ $root.currentTab('table-relationships'); }">${_('Relationships')} (<span data-bind="text: $root.database().table().relationshipsDetails().inputs().length + $root.database().table().relationshipsDetails().targets().length"></span>)</a></li>
  797. ##<!-- /ko -->
  798. <!-- /ko -->
  799. <li><a href="#details" data-toggle="tab" data-bind="click: function(){ $root.currentTab('table-details'); }">${ _('Details') }</a></li>
  800. </ul>
  801. <div class="tab-content margin-top-10" style="border: none; overflow: hidden">
  802. <div class="tab-pane" id="overview">
  803. <!-- ko if: $root.currentTab() == 'table-overview' -->
  804. <!-- ko template: 'metastore-overview-tab' --><!-- /ko -->
  805. <!-- /ko -->
  806. </div>
  807. <div class="tab-pane" id="columns">
  808. <!-- ko if: $root.currentTab() == 'table-columns' -->
  809. <input class="input-xlarge search-query margin-left-10" type="text" placeholder="${ _('Search for a column...') }" data-bind="clearable: columnQuery, value: columnQuery, valueUpdate: 'afterkeydown'"/>
  810. <!-- ko template: 'metastore-columns-tab' --><!-- /ko -->
  811. <!-- /ko -->
  812. </div>
  813. <div class="tab-pane" id="partitions">
  814. <!-- ko if: $root.currentTab() == 'table-partitions' -->
  815. <!-- ko template: 'metastore-partitions-tab' --><!-- /ko -->
  816. <!-- /ko -->
  817. </div>
  818. <div class="tab-pane" id="sample">
  819. <!-- ko if: $root.currentTab() == 'table-sample' -->
  820. <!-- ko template: 'metastore-sample-tab' --><!-- /ko -->
  821. <!-- /ko -->
  822. </div>
  823. <div class="tab-pane" id="permissions">
  824. <div class="acl-panel-content" style="height: 988px;">
  825. <div class="pull-right">
  826. <input class="input-medium no-margin" type="text" placeholder="Search privileges..."> &nbsp;
  827. <a class="btn pointer">
  828. <i class="fa fa-plus-circle"></i> Add role
  829. </a>
  830. </div>
  831. <h4 style="margin-top: 4px;">Privileges &nbsp;</h4>
  832. <div class="acl-block-title">
  833. <i class="fa fa-cube muted"></i> <a class="pointer"><span>customerFraud</span></a>
  834. </div>
  835. <div>
  836. <div class="acl-block acl-block-airy">
  837. <span class="muted" title="3 months ago">TABLE</span>
  838. <span>
  839. <a class="muted" target="_blank" style="margin-left: 4px" title="Open in Sentry" href="/security/hive"><i class="fa fa-external-link"></i></a>
  840. </span>
  841. <br>
  842. server=<span>server1</span>
  843. <span>
  844. <i class="fa fa-long-arrow-right"></i> db=<a class="pointer" title="Browse db privileges"><span data-bind="text: $root.database().name"></span></a>
  845. </span>
  846. <span>
  847. <i class="fa fa-long-arrow-right"></i> table=<a class="pointer" title="Browse table privileges"><span data-bind="text: name"></span></a>
  848. </span>
  849. <span style="display: none;">
  850. <i class="fa fa-long-arrow-right"></i> column=<a class="pointer" title="Browse column privileges"><span></span></a>
  851. </span>
  852. <i class="fa fa-long-arrow-right"></i> action=INSERT
  853. </div>
  854. <div class="acl-block acl-block-airy">
  855. <span class="muted" title="3 months ago">TABLE</span>
  856. <span>
  857. <a class="muted" target="_blank" style="margin-left: 4px" title="Open in Sentry" href="/security/hive"><i class="fa fa-external-link"></i></a>
  858. </span>
  859. <br>
  860. server=server1
  861. <span>
  862. <i class="fa fa-long-arrow-right"></i> db=<a class="pointer" title="Browse db privileges"><span data-bind="text: $root.database().name"></span></a>
  863. </span>
  864. <span>
  865. <i class="fa fa-long-arrow-right"></i> table=<a class="pointer" title="Browse table privileges"><span data-bind="text: name"></span></a>
  866. </span>
  867. <span style="display: none;">
  868. <i class="fa fa-long-arrow-right"></i> column=<a class="pointer" title="Browse column privileges"><span></span></a>
  869. </span>
  870. <i class="fa fa-long-arrow-right"></i> action=<span>SELECT</span>
  871. </div>
  872. </div>
  873. <div class="acl-block acl-actions">
  874. <span class="pointer" title="Show 50 more..." style="display: none;"><i class="fa fa-ellipsis-h"></i></span>
  875. <span class="pointer" title="Add privilege"><i class="fa fa-plus"></i></span>
  876. <span class="pointer" title="Undo" style="display: none;"> &nbsp; <i class="fa fa-undo"></i></span>
  877. <span class="pointer" title="Save" style="display: none;"> &nbsp; <i class="fa fa-save"></i></span>
  878. </div>
  879. <div class="acl-block-title">
  880. <i class="fa fa-cube muted"></i> <a class="pointer"><span>customerAccess</span></a>
  881. </div>
  882. <div>
  883. <div class="acl-block acl-block-airy">
  884. <span class="muted" title="3 months ago">TABLE</span>
  885. <span>
  886. <a class="muted" target="_blank" style="margin-left: 4px" title="Open in Sentry" href="/security/hive"><i class="fa fa-external-link"></i></a>
  887. </span>
  888. <br>
  889. server=server1
  890. <span>
  891. <i class="fa fa-long-arrow-right"></i> db=<a class="pointer" title="Browse db privileges"><span data-bind="text: $root.database().name"></span></a>
  892. </span>
  893. <span>
  894. <i class="fa fa-long-arrow-right"></i> table=<a class="pointer" title="Browse table privileges"><span data-bind="text: name"></span></a>
  895. </span>
  896. <span style="display: none;">
  897. <i class="fa fa-long-arrow-right"></i> column=<a class="pointer" title="Browse column privileges"><span></span></a>
  898. </span>
  899. <i class="fa fa-long-arrow-right"></i> action=<span>ALL</span>
  900. </div>
  901. <div class="acl-block acl-actions">
  902. <span class="pointer" title="Show 50 more..." style="display: none;"><i class="fa fa-ellipsis-h"></i></span>
  903. <span class="pointer" title="Add privilege"><i class="fa fa-plus"></i></span>
  904. <span class="pointer" title="Undo" style="display: none;"> &nbsp; <i class="fa fa-undo"></i></span>
  905. <span class="pointer" title="Save" style="display: none;"> &nbsp; <i class="fa fa-save"></i></span>
  906. </div>
  907. </div>
  908. </div>
  909. </div>
  910. <div class="tab-pane" id="queries">
  911. <!-- ko if: $root.optimizerEnabled() && $root.currentTab() == 'table-queries' -->
  912. <!-- ko template: {name: 'metastore-queries-tab', data: $root.database().table()} --><!-- /ko -->
  913. <!-- /ko -->
  914. </div>
  915. <div class="tab-pane" id="joins">
  916. <!-- ko if: $root.optimizerEnabled() && $root.database().table().optimizerDetails() -->
  917. <!-- ko with: $root.database().table().optimizerDetails().joinTables -->
  918. <table data-bind="visible: $data.length > 0" class="table table-condensed">
  919. <thead>
  920. <tr>
  921. <th width="10%">${ _('Id') }</th>
  922. <th width="10%">${ _('Popularity') }</th>
  923. <th width="30%">${ _('Table Name') }</th>
  924. <th>${ _('Join Column') }</th>
  925. <th width="10%">${ _('Join counts') }</th>
  926. </tr>
  927. </thead>
  928. <tbody data-bind="hueach: {data: $data, itemHeight: 32, scrollable: '${ MAIN_SCROLLABLE }', scrollableOffset: 200}">
  929. <tr>
  930. <td class="pointer" data-bind="text: tableEid, click: function(){ window.open($root.optimizerUrl() + '#/table/' + tableEid(), '_blank'); }"></td>
  931. <td style="height: 10px; width: 70px; margin-top:5px;" data-bind="attr: {'title': joinpercent()}">
  932. <div class="progress bar" style="background-color: #0B7FAD" data-bind="style: { 'width' : joinpercent() + '%' }"></div>
  933. </td>
  934. <td><a data-bind="text: tableName, attr: { href: '/metastore/table/' + $root.database().name + '/' + tableName() }"></a></td>
  935. <td class="pointer"><code data-bind="text: joinColumns, click: scrollToColumn"></code></td>
  936. <td data-bind="text: numJoins"></td>
  937. </tr>
  938. </tbody>
  939. </table>
  940. <!-- /ko -->
  941. <!-- /ko -->
  942. </div>
  943. <div class="tab-pane" id="relationships">
  944. <!-- ko if: $root.currentTab() == 'table-relationships' && $root.database().table().relationshipsDetails() -->
  945. <!-- ko with: $root.database().table().relationshipsDetails() -->
  946. <h4>${ _('Inputs') }</h4>
  947. <div class="row-fluid">
  948. <!-- ko foreach: inputs -->
  949. <div data-bind="text: $data"></div>
  950. <!-- /ko -->
  951. <!-- ko if: inputs().length == 0 -->
  952. ${ _('Not inputs') }
  953. <!-- /ko -->
  954. </div>
  955. </br>
  956. <h4>${ _('Targets') }</h4>
  957. <div class="row-fluid">
  958. <!-- ko foreach: targets -->
  959. <div data-bind="text: $data"></div>
  960. <!-- /ko -->
  961. <!-- ko if: targets().length == 0 -->
  962. ${ _('Not targets') }
  963. <!-- /ko -->
  964. </div>
  965. </br>
  966. <h4>${ _('Source query') }</h4>
  967. <div class="row-fluid">
  968. <!-- ko if: source_query().length > 0 -->
  969. <code data-bind="text: source_query"></code>
  970. <!-- /ko -->
  971. <!-- ko if: source_query().length == 0 -->
  972. ${ _('No source query') }
  973. <!-- /ko -->
  974. </div>
  975. </br>
  976. <h4>${ _('Target queries') }</h4>
  977. <div class="row-fluid">
  978. <!-- ko foreach: target_queries -->
  979. <div>
  980. <code data-bind="text: $data"></code>
  981. </div>
  982. <!-- /ko -->
  983. <!-- ko if: target_queries().length == 0 -->
  984. ${ _('Not target queries') }
  985. <!-- /ko -->
  986. </div>
  987. </br>
  988. <h4>${ _('Lineage') }</h4>
  989. <div class="row-fluid">
  990. <button class="btn toolbarBtn" title="${_('Open in Navigator ')}" data-bind="click: function () { window.open($root.navigatorUrl() + '?view=detailsView&id=' + id() + '&b=rFlCX&tab=lineage', '_blank'); }">
  991. <i class="fa fa-skyatlas"></i> ${_('View in Navigator')}
  992. </button>
  993. </div>
  994. <!-- /ko -->
  995. <!-- /ko -->
  996. </div>
  997. <div class="tab-pane" id="details">
  998. <!-- ko if: $root.currentTab() == 'table-details' -->
  999. <!-- ko template: 'metastore-details-tab' --><!-- /ko -->
  1000. <!-- /ko -->
  1001. </div>
  1002. </div>
  1003. </script>
  1004. <span id="metastoreComponents">
  1005. % if not is_embeddable:
  1006. <a title="${_('Toggle Assist')}" class="pointer show-assist" data-bind="visible: !$root.isLeftPanelVisible() && $root.assistAvailable(), click: function () { $root.isLeftPanelVisible(true); }">
  1007. <i class="fa fa-chevron-right"></i>
  1008. </a>
  1009. % endif
  1010. <div class="main-content">
  1011. <div class="vertical-full container-fluid" data-bind="style: { 'padding-left' : $root.isLeftPanelVisible() ? '0' : '20px' }">
  1012. <div class="vertical-full row-fluid panel-container">
  1013. % if not is_embeddable:
  1014. <div class="assist-container left-panel" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable()">
  1015. <a title="${_('Toggle Assist')}" class="pointer hide-assist" data-bind="click: function() { $root.isLeftPanelVisible(false) }">
  1016. <i class="fa fa-chevron-left"></i>
  1017. </a>
  1018. <div class="assist" data-bind="component: {
  1019. name: 'assist-panel',
  1020. params: {
  1021. user: '${user.username}',
  1022. sql: {
  1023. navigationSettings: {
  1024. openItem: true,
  1025. showStats: true
  1026. }
  1027. },
  1028. visibleAssistPanels: ['sql']
  1029. }
  1030. }"></div>
  1031. </div>
  1032. <div class="resizer" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable(), splitDraggable : { appName: 'notebook', leftPanelVisible: $root.isLeftPanelVisible }"><div class="resize-bar">&nbsp;</div></div>
  1033. % endif
  1034. <div class="content-panel" ${ not is_embeddable and 'data-bind="niceScroll"' or ''}>
  1035. <div class="metastore-main">
  1036. <h1>
  1037. <!-- ko template: { if: database() !== null && database().table() !== null, name: 'metastore-describe-table-actions' }--><!-- /ko -->
  1038. <!-- ko template: { if: database() !== null && database().table() === null, name: 'metastore-tables-actions' }--><!-- /ko -->
  1039. <!-- ko template: { if: database() === null, name: 'metastore-databases-actions' }--><!-- /ko -->
  1040. <!-- ko template: 'metastore-breadcrumbs' --><!-- /ko -->
  1041. </h1>
  1042. <i data-bind="visible: loading" class="fa fa-spinner fa-spin fa-2x margin-left-10" style="color: #999; display: none;"></i>
  1043. <!-- ko template: { if: !loading() && database() === null, name: 'metastore-databases' } --><!-- /ko -->
  1044. <!-- ko with: database -->
  1045. <i data-bind="visible: loading" class="fa fa-spinner fa-spin fa-2x margin-left-10" style="color: #999; display: none;"></i>
  1046. <!-- ko template: { if: !loading() && table() === null, name: 'metastore-tables' } --><!-- /ko -->
  1047. <!-- ko with: table -->
  1048. <!-- ko template: 'metastore-describe-table' --><!-- /ko -->
  1049. <!-- /ko -->
  1050. <!-- /ko -->
  1051. </div>
  1052. </div>
  1053. </div>
  1054. </div>
  1055. <div id="dropSingleTable" class="modal hide fade">
  1056. % if is_embeddable:
  1057. <form data-bind="submit: dropAndWatch" method="POST">
  1058. <input type="hidden" name="is_embeddable" value="true"/>
  1059. % else:
  1060. <form method="POST">
  1061. % endif
  1062. ${ csrf_token(request) | n,unicode }
  1063. <div class="modal-header">
  1064. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  1065. <h2 class="modal-title">${_('Drop Table')}</h2>
  1066. </div>
  1067. <div class="modal-body">
  1068. <div>${_('Do you really want to drop the table')} <span style="font-weight: bold;" data-bind="text: database() && database().table() ? database().table().name : ''"></span>?</div>
  1069. </div>
  1070. <div class="modal-footer">
  1071. <input type="hidden" name="table_selection" data-bind="value: database() && database().table() ? database().table().name : ''" />
  1072. <input type="button" class="btn" data-dismiss="modal" value="${_('No')}"/>
  1073. <input type="submit" data-bind="click: function (vm, e) { var $form = $(e.target).parents('form'); $form.attr('action', '/metastore/tables/drop/' + vm.database().name); return true; }" class="btn btn-danger" value="${_('Yes, drop this table')}"/>
  1074. </div>
  1075. </form>
  1076. </div>
  1077. <div id="import-data-modal" class="modal hide fade" style="display: block;width: 640px;margin-left: -320px!important;"></div>
  1078. </div>
  1079. </span>
  1080. <script type="text/javascript">
  1081. function dropAndWatch(formElement) {
  1082. $(formElement).ajaxSubmit({
  1083. dataType: 'json',
  1084. success: function(resp) {
  1085. if (resp.history_uuid) {
  1086. huePubSub.publish('notebook.task.submitted', resp.history_uuid);
  1087. } else if (resp && resp.message) {
  1088. $(document).trigger("error", resp.message);
  1089. }
  1090. $("#dropTable").modal('hide');
  1091. $("#dropSingleTable").modal('hide');
  1092. $("#dropDatabase").modal('hide');
  1093. },
  1094. error: function (xhr, textStatus, errorThrown) {
  1095. $(document).trigger("error", xhr.responseText);
  1096. }
  1097. });
  1098. }
  1099. function browsePartitionFolder(url) {
  1100. $.get(url, {
  1101. format: "json"
  1102. },function(resp) {
  1103. if (resp.uri_path) {
  1104. huePubSub.publish('open.link', resp.uri_path);
  1105. } else if (resp.message) {
  1106. $(document).trigger("error", resp.message);
  1107. }
  1108. }).fail(function (xhr) {
  1109. $(document).trigger("error", xhr.responseText);
  1110. });
  1111. }
  1112. function queryAndWatch(url) {
  1113. $.post(url, {
  1114. format: "json"
  1115. },function(resp) {
  1116. if (resp.history_uuid) {
  1117. huePubSub.publish('open.editor.query', resp.history_uuid);
  1118. } else if (resp.message) {
  1119. $(document).trigger("error", resp.message);
  1120. }
  1121. }).fail(function (xhr) {
  1122. $(document).trigger("error", xhr.responseText);
  1123. });
  1124. }
  1125. (function () {
  1126. ko.options.deferUpdates = true;
  1127. $(document).ready(function () {
  1128. var options = {
  1129. user: '${ user.username }',
  1130. % if is_embeddable:
  1131. hue4: true,
  1132. % endif
  1133. optimizerEnabled: '${ is_optimizer_enabled }' === 'True',
  1134. navigatorEnabled: '${ is_navigator_enabled }' === 'True',
  1135. optimizerUrl: '${ optimizer_url }',
  1136. navigatorUrl: '${ navigator_url }'
  1137. };
  1138. var viewModel = new MetastoreViewModel(options);
  1139. huePubSub.subscribe('metastore.scroll.to.top', function () {
  1140. $('${ MAIN_SCROLLABLE }').scrollTop(0);
  1141. $('${ MAIN_SCROLLABLE }').getNiceScroll().resize();
  1142. });
  1143. viewModel.currentTab.subscribe(function(tab){
  1144. if (tab == 'table-relationships') {
  1145. // viewModel.database().table().getRelationships();
  1146. } else if (tab == 'table-sample') {
  1147. var selector = '#sample .sample-table';
  1148. if ($(selector).parents('.dataTables_wrapper').length == 0){
  1149. hueUtils.waitForRendered(selector, function(el){ return el.find('td').length > 0 }, function(){
  1150. $(selector).dataTable({
  1151. "bPaginate": false,
  1152. "bLengthChange": false,
  1153. "bInfo": false,
  1154. "bDestroy": true,
  1155. "bFilter": false,
  1156. "bAutoWidth": false,
  1157. "oLanguage": {
  1158. "sEmptyTable": "${_('No data available')}",
  1159. "sZeroRecords": "${_('No matching records')}"
  1160. },
  1161. "fnDrawCallback": function (oSettings) {
  1162. $(selector).parents('.dataTables_wrapper').css('overflow-x', 'hidden');
  1163. $(selector).jHueTableExtender({
  1164. fixedHeader: true,
  1165. fixedFirstColumn: true,
  1166. includeNavigator: false,
  1167. parentId: 'sample',
  1168. classToRemove: 'sample-table',
  1169. mainScrollable: '${ MAIN_SCROLLABLE }',
  1170. stickToTopPosition: 73,
  1171. clonedContainerPosition: 'fixed',
  1172. app: 'metastore'
  1173. });
  1174. $(selector).jHueHorizontalScrollbar();
  1175. },
  1176. "aoColumnDefs": [
  1177. {
  1178. "sType": "numeric",
  1179. "aTargets": [ "sort-numeric" ]
  1180. },
  1181. {
  1182. "sType": "string",
  1183. "aTargets": [ "sort-string" ]
  1184. },
  1185. {
  1186. "sType": "date",
  1187. "aTargets": [ "sort-date" ]
  1188. }
  1189. ]
  1190. });
  1191. });
  1192. }
  1193. }
  1194. $('${ MAIN_SCROLLABLE }').getNiceScroll().resize();
  1195. });
  1196. ko.applyBindings(viewModel, $('#metastoreComponents')[0]);
  1197. if (location.getParameter('refresh') === 'true') {
  1198. huePubSub.publish('assist.db.refresh', { sourceType: 'hive' });
  1199. hueUtils.replaceURL('?');
  1200. }
  1201. window.scrollToColumn = function (col) {
  1202. if (!col.table.samples.loading()) {
  1203. $('a[href="#sample"]').click();
  1204. hueUtils.waitForRendered('#sample .sample-table', function (el) {
  1205. return el.parent().hasClass('dataTables_wrapper')
  1206. }, function () {
  1207. var sampleTable = $('#sample .sample-table');
  1208. var sampleCol = sampleTable.find('th').filter(function () {
  1209. return $.trim($(this).text()).indexOf(col.name()) > -1;
  1210. });
  1211. sampleTable.find('.columnSelected').removeClass('columnSelected');
  1212. sampleTable.find('tr td:nth-child(' + (sampleCol.index() + 1) + ')').addClass('columnSelected');
  1213. var scrollLeft = 0;
  1214. sampleTable.find('th:lt(' + sampleCol.index() + ')').each(function () {
  1215. scrollLeft += $(this).outerWidth();
  1216. });
  1217. scrollLeft = Math.max(0, scrollLeft - 40);
  1218. sampleTable.parent().scrollLeft(scrollLeft);
  1219. sampleTable.parent().trigger('scroll_update');
  1220. });
  1221. }
  1222. }
  1223. });
  1224. })();
  1225. </script>
  1226. </span>
  1227. % if not is_embeddable:
  1228. ${ commonfooter(request, messages) | n,unicode }
  1229. % endif