assist.mako 146 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159
  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.translation import ugettext as _
  18. from metadata.conf import has_navigator, OPTIMIZER
  19. from metastore.conf import ENABLE_NEW_CREATE_TABLE
  20. from notebook.conf import ENABLE_QUERY_BUILDER, ENABLE_QUERY_SCHEDULING, get_ordered_interpreters
  21. from dashboard.conf import HAS_SQL_ENABLED
  22. from desktop import appmanager
  23. from desktop import conf
  24. from desktop.conf import IS_EMBEDDED, USE_NEW_SIDE_PANELS, VCS
  25. from desktop.lib.i18n import smart_unicode
  26. from desktop.views import _ko
  27. %>
  28. <%def name="assistJSModels()">
  29. <script src="${ static('desktop/js/assist/assistDbEntry.js') }"></script>
  30. <script src="${ static('desktop/js/assist/assistDbSource.js') }"></script>
  31. <script src="${ static('desktop/js/assist/assistStorageEntry.js') }"></script>
  32. <script src="${ static('desktop/js/assist/assistGitEntry.js') }"></script>
  33. <script src="${ static('desktop/js/assist/assistHBaseEntry.js') }"></script>
  34. <script src="${ static('desktop/js/document/hueDocument.js') }"></script>
  35. <script src="${ static('desktop/js/document/hueFileEntry.js') }"></script>
  36. </%def>
  37. <%def name="assistPanel(is_s3_enabled=False)">
  38. <%
  39. # TODO remove
  40. try:
  41. home_dir = user.get_home_directory()
  42. if not request.fs.isdir(home_dir):
  43. home_dir = '/'
  44. except:
  45. home_dir = '/'
  46. %>
  47. <script type="text/html" id="assist-no-database-entries">
  48. <ul class="assist-tables">
  49. <li>
  50. <span class="assist-no-entries">${_('The database has no tables')}</span>
  51. </li>
  52. </ul>
  53. </script>
  54. <script type="text/html" id="assist-no-table-entries">
  55. <ul>
  56. <li>
  57. <span class="assist-no-entries">${_('The table has no columns')}</span>
  58. </li>
  59. </ul>
  60. </script>
  61. <script type="text/html" id="assist-database-actions">
  62. <div class="assist-actions database-actions" style="opacity: 0">
  63. %if has_navigator(user):
  64. <!-- ko if: sourceType === 'hive' || sourceType === 'impala' -->
  65. <a class="inactive-action" href="javascript:void(0)" data-bind="visible: navigationSettings.showStats, click: function (data, event) { showContextPopover(data, event); }, css: { 'blue': statsVisible }"><i class="fa fa-fw fa-info" title="${_('Show details')}"></i></a>
  66. <!-- /ko -->
  67. %endif
  68. <a class="inactive-action" href="javascript:void(0)" data-bind="visible: navigationSettings.openItem, click: openItem"><i class="fa fa-long-arrow-right" title="${_('Open')}"></i></a>
  69. </div>
  70. </script>
  71. <script type="text/html" id="collection-title-context-items">
  72. <li><a href="javascript:void(0);" data-bind="hueLink: '/indexer'"><i class="fa fa-fw fa-table"></i> ${ _('Open in Browser') }</a></li>
  73. </script>
  74. <script type="text/html" id="sql-context-items">
  75. <!-- ko if: typeof catalogEntry !== 'undefined' -->
  76. <!-- ko if: sourceType === 'solr' -->
  77. <li><a href="javascript:void(0);" data-bind="click: function (data) { showContextPopover(data, { target: $parentContext.$contextSourceElement }, { left: 4, top: 2 }); }"><i class="fa fa-fw fa-info"></i> ${ _('Show details') }</a></li>
  78. <!-- ko if: catalogEntry.isTableOrView() -->
  79. <li><a href="javascript:void(0);" data-bind="click: openInIndexer"><i class="fa fa-fw fa-table"></i> ${ _('Open in Browser') }</a></li>
  80. <li>
  81. <a href="javascript: void(0);" data-bind="click: function() { explore(true); }">
  82. <!-- ko template: { name: 'app-icon-template', data: { icon: 'dashboard' } } --><!-- /ko --> ${ _('Open in Dashboard') }
  83. </a>
  84. </li>
  85. <!-- /ko -->
  86. <!-- /ko -->
  87. <!-- ko ifnot: sourceType === 'solr' -->
  88. <li><a href="javascript:void(0);" data-bind="click: function (data) { showContextPopover(data, { target: $parentContext.$contextSourceElement }, { left: 4, top: 2 }); }"><i class="fa fa-fw fa-info"></i> ${ _('Show details') }</a></li>
  89. <!-- ko if: !catalogEntry.isDatabase() && $currentApp() === 'editor' -->
  90. <li><a href="javascript:void(0);" data-bind="click: dblClick"><i class="fa fa-fw fa-paste"></i> ${ _('Insert at cursor') }</a></li>
  91. <!-- /ko -->
  92. % if not IS_EMBEDDED.get():
  93. <!-- ko if: catalogEntry.path.length <=2 -->
  94. <li><a href="javascript:void(0);" data-bind="click: openInMetastore"><i class="fa fa-fw fa-table"></i> ${ _('Open in Browser') }</a></li>
  95. <!-- /ko -->
  96. % endif
  97. <!-- ko if: catalogEntry.isTableOrView() -->
  98. <li>
  99. <a href="javascript:void(0);" data-bind="click: function() { huePubSub.publish('query.and.watch', {'url': '/notebook/browse/' + databaseName + '/' + tableName + '/', sourceType: sourceType}); }">
  100. <i class="fa fa-fw fa-code"></i> ${ _('Open in Editor') }
  101. </a>
  102. </li>
  103. % if HAS_SQL_ENABLED.get():
  104. <li>
  105. <a href="javascript: void(0);" data-bind="click: function() { explore(false); }">
  106. <!-- ko template: { name: 'app-icon-template', data: { icon: 'dashboard' } } --><!-- /ko --> ${ _('Open in Dashboard') }
  107. </a>
  108. </li>
  109. % endif
  110. <!-- /ko -->
  111. %if ENABLE_QUERY_BUILDER.get():
  112. <!-- ko if: catalogEntry.isColumn() && $currentApp() === 'editor' -->
  113. <li class="divider"></li>
  114. <!-- ko template: { name: 'query-builder-context-items' } --><!-- /ko -->
  115. <!-- /ko -->
  116. %endif
  117. <!-- /ko -->
  118. <!-- /ko -->
  119. </script>
  120. <script type="text/html" id="query-builder-context-items">
  121. <li data-bind="contextSubMenu: '.hue-context-sub-menu'">
  122. <a href="javascript:void(0);"><i class="fa fa-fw fa-magic"></i> ${ _('Project') }<i class="sub-icon fa fa-fw fa-chevron-right"></i></a>
  123. <ul class="hue-context-menu hue-context-sub-menu">
  124. <li><a href="javascript:void(0);" data-bind="click: function () { QueryBuilder.addRule(databaseName, tableName, columnName, 'Select', 'Project', '{0}', false, false); }">${ _('Select') }</a></li>
  125. <li><a href="javascript:void(0);" data-bind="click: function () { QueryBuilder.addRule(databaseName, tableName, columnName, 'Select distinct', 'Project', '{0}', false, false); }">${ _('Select distinct') }</a></li>
  126. </ul>
  127. </li>
  128. <li data-bind="contextSubMenu: '.hue-context-sub-menu'">
  129. <a href="javascript:void(0);"><i class="fa fa-fw fa-magic"></i> ${ _('Aggregate') }<i class="sub-icon fa fa-fw fa-chevron-right"></i></a>
  130. <ul class="hue-context-menu hue-context-sub-menu">
  131. <li><a href="javascript:void(0);" data-bind="click: function () { QueryBuilder.addRule(databaseName, tableName, columnName, 'Count', 'Aggregate', 'COUNT({0}.{1}) as count_{1}', false, false); }">${ _('Count') }</a></li>
  132. <li><a href="javascript:void(0);" data-bind="click: function () { QueryBuilder.addRule(databaseName, tableName, columnName, 'Count distinct', 'Aggregate', 'COUNT(DISTINCT {0}.{1}) as distinct_count_{1}', false, false); }">${ _('Count distinct') }</a></li>
  133. <li><a href="javascript:void(0);" data-bind="click: function () { QueryBuilder.addRule(databaseName, tableName, columnName, 'Sum', 'Aggregate', 'SUM({0}.{1}) as sum_{1}', false, false); }">${ _('Sum') }</a></li>
  134. <li><a href="javascript:void(0);" data-bind="click: function () { QueryBuilder.addRule(databaseName, tableName, columnName, 'Minimum', 'Aggregate', 'MIN({0}.{1}) as min_{1}', false, false); }">${ _('Minimum') }</a></li>
  135. <li><a href="javascript:void(0);" data-bind="click: function () { QueryBuilder.addRule(databaseName, tableName, columnName, 'Maximum', 'Aggregate', 'MAX({0}.{1}) as max_{1}', false, false); }">${ _('Maximum') }</a></li>
  136. <li><a href="javascript:void(0);" data-bind="click: function () { QueryBuilder.addRule(databaseName, tableName, columnName, 'Average', 'Aggregate', 'AVG({0}.{1}) as avg_{1}', false, false); }">${ _('Average') }</a></li>
  137. </ul>
  138. </li>
  139. <li data-bind="contextSubMenu: '.hue-context-sub-menu'">
  140. <a href="javascript:void(0);"><i class="fa fa-fw fa-magic"></i> ${ _('Filter') }<i class="sub-icon fa fa-fw fa-chevron-right"></i></a>
  141. <ul class="hue-context-menu hue-context-sub-menu">
  142. <li><a href="javascript:void(0);" data-bind="click: function () { var isNotNullRule = QueryBuilder.getRule(databaseName, tableName, columnName, 'Is not null'); if (isNotNullRule.length) { isNotNullRule.attr('rule', 'Is null'); isNotNullRule.find('.rule').text('Is null'); } else { QueryBuilder.addRule(databaseName, tableName, columnName, 'Is null', 'Filter', '{0}.{1} = null', false, false); } }">${ _('Is null') }</a></li>
  143. <li><a href="javascript:void(0);" data-bind="click: function () { var isNullRule = QueryBuilder.getRule(databaseName, tableName, columnName, 'Is null'); if (isNullRule.length) { isNullRule.attr('rule', 'Is not null'); isNullRule.find('.rule').text('Is not null'); } else { QueryBuilder.addRule(databaseName, tableName, columnName, 'Is not null', 'Filter', '{0}.{1} != null', false, false); } }">${ _('Is not null') }</a></li>
  144. <li><a href="javascript:void(0);" data-bind="click: function () { QueryBuilder.addRule(databaseName, tableName, columnName, 'Equal to', 'Filter', '{0}.{1} = {2}', true, true); }">${ _('Equal to') }</a></li>
  145. <li><a href="javascript:void(0);" data-bind="click: function () { QueryBuilder.addRule(databaseName, tableName, columnName, 'Not equal to', 'Filter', '{0}.{1} != {2}', true, true); }">${ _('Not equal to') }</a></li>
  146. <li><a href="javascript:void(0);" data-bind="click: function () { QueryBuilder.addRule(databaseName, tableName, columnName, 'Greater than', 'Filter', '{0}.{1} > {2}', true, false) }">${ _('Greater than') }</a></li>
  147. <li><a href="javascript:void(0);" data-bind="click: function () { QueryBuilder.addRule(databaseName, tableName, columnName, 'Less than', 'Filter', '{0}.{1} < {2}', true, false); }">${ _('Less than') }</a></li>
  148. </ul>
  149. </li>
  150. <li data-bind="contextSubMenu: '.hue-context-sub-menu'">
  151. <a href="javascript:void(0);"><i class="fa fa-fw fa-magic"></i> ${ _('Order') }<i class="sub-icon fa fa-fw fa-chevron-right"></i></a>
  152. <ul class="hue-context-menu hue-context-sub-menu">
  153. <li><a href="javascript:void(0);" data-bind="click: function () { var descendingRule = QueryBuilder.getRule(databaseName, tableName, columnName, 'Descending'); if (descendingRule.length) { descendingRule.attr('rule', 'Ascending'); descendingRule.find('.rule').text('Ascending'); } else { QueryBuilder.addRule(databaseName, tableName, columnName, 'Ascending', 'Order', '{0}.{1} ASC', false, false); } }">${ _('Ascending') }</a></li>
  154. <li><a href="javascript:void(0);" data-bind="click: function () { var ascendingRule = QueryBuilder.getRule(databaseName, tableName, columnName, 'Ascending'); if (ascendingRule.length) { ascendingRule.attr('rule', 'Descending'); ascendingRule.find('.rule').text('Descending'); } else { QueryBuilder.addRule(databaseName, tableName, columnName, 'Descending', 'Order', '{0}.{1} DESC', false, false); } }">${ _('Descending') }</a></li>
  155. </ul>
  156. </li>
  157. </script>
  158. <script type="text/html" id="hdfs-context-items">
  159. <li><a href="javascript:void(0);" data-bind="hueLink: definition.url"><i class="fa fa-fw" data-bind="css: {'fa-folder-open-o': definition.type === 'dir', 'fa-file-text-o': definition.type === 'file'}"></i> ${ _('Open in Browser') }</a></li>
  160. <!-- ko if: IS_HUE_4 && definition.type === 'file' -->
  161. <li><a href="javascript:void(0);" data-bind="click: openInImporter"><!-- ko template: { name: 'app-icon-template', data: { icon: 'importer' } } --><!-- /ko --> ${ _('Open in Importer') }</a></li>
  162. <!-- /ko -->
  163. <!-- ko if: $currentApp() === 'editor' -->
  164. <li><a href="javascript:void(0);" data-bind="click: dblClick"><i class="fa fa-fw fa-paste"></i> ${ _('Insert at cursor') }</a></li>
  165. <!-- /ko -->
  166. </script>
  167. <script type="text/html" id="document-context-items">
  168. <!-- ko if: definition().type === 'directory' -->
  169. <li><a href="javascript: void(0);" data-bind="click: open"><i class="fa fa-fw fa-folder-open-o"></i> ${ _('Open folder') }</a></li>
  170. <!-- /ko -->
  171. <!-- ko if: definition().type !== 'directory' -->
  172. <li><a href="javascript: void(0);" data-bind="click: function(data) { showContextPopover(data, { target: $parentContext.$contextSourceElement }, { left: 4, top: 2 }); }"><i class="fa fa-fw fa-info"></i> ${ _('Show details') }</a></li>
  173. <li><a href="javascript: void(0);" data-bind="click: open"><i class="fa fa-fw fa-edit"></i> ${ _('Open document') }</a></li>
  174. <!-- /ko -->
  175. </script>
  176. <script type="text/html" id="hbase-context-items">
  177. <!-- ko if: definition.host -->
  178. <li><a href="javascript: void(0);" data-bind="click: open"><i class="fa fa-fw fa-folder-open-o"></i> ${ _('Open cluster') }</a></li>
  179. <!-- /ko -->
  180. <!-- ko ifnot: definition.host -->
  181. <li><a href="javascript: void(0);" data-bind="click: open"><!-- ko template: { name: 'app-icon-template', data: { icon: 'hbase' } } --><!-- /ko --> ${ _('Open in HBase') }</a></li>
  182. <!-- /ko -->
  183. </script>
  184. <script type="text/html" id="assist-database-entry">
  185. <li class="assist-table" data-bind="appAwareTemplateContextMenu: { template: 'sql-context-items', scrollContainer: '.assist-db-scrollable' }, visibleOnHover: { selector: '.database-actions' }">
  186. <!-- ko template: { name: 'assist-database-actions' } --><!-- /ko -->
  187. <a class="assist-table-link" href="javascript: void(0);" data-bind="click: function () { $parent.selectedDatabase($data); $parent.selectedDatabaseChanged(); }, attr: {'title': catalogEntry.getTitle() }, draggableText: { text: editorText, meta: {'type': 'sql', 'database': databaseName} }"><i class="fa fa-fw fa-database muted valign-middle"></i> <span class="highlightable" data-bind="text: catalogEntry.name, css: { 'highlight': highlight() }"></span></a>
  188. </li>
  189. </script>
  190. <script type="text/html" id="assist-table-entry">
  191. <li class="assist-table" data-bind="appAwareTemplateContextMenu: { template: 'sql-context-items', scrollContainer: '.assist-db-scrollable' }, visibleOnHover: { override: statsVisible() || navigationSettings.rightAssist, selector: '.table-actions' }">
  192. <div class="assist-actions table-actions" data-bind="css: { 'assist-actions-left': navigationSettings.rightAssist }" style="opacity: 0">
  193. <a class="inactive-action" href="javascript:void(0)" data-bind="visible: navigationSettings.showStats, click: showContextPopover, css: { 'blue': statsVisible }"><i class="fa fa-fw fa-info" title="${_('Show details')}"></i></a>
  194. <a class="inactive-action" href="javascript:void(0)" data-bind="visible: navigationSettings.openItem, click: openItem"><i class="fa fa-long-arrow-right" title="${_('Open')}"></i></a>
  195. </div>
  196. <a class="assist-entry assist-table-link" href="javascript:void(0)" data-bind="click: toggleOpen, attr: {'title': catalogEntry.getTitle() }, draggableText: { text: editorText, meta: {'type': 'sql', 'isView': catalogEntry.isView(), 'table': tableName, 'database': databaseName} }">
  197. <i class="fa fa-fw fa-table muted valign-middle" data-bind="css: { 'fa-eye': catalogEntry.isView() && !navigationSettings.rightAssist, 'fa-table': catalogEntry.isTable() && sourceType !== 'solr' && !navigationSettings.rightAssist, 'fa-search': sourceType === 'solr' }"></i>
  198. <span class="highlightable" data-bind="text: catalogEntry.getDisplayName(navigationSettings.rightAssist), css: { 'highlight': highlight }"></span> <!-- ko if: assistDbSource.activeSort() === 'popular' && popularity() > 0 --><i title="${ _('Popular') }" class="fa fa-star-o top-star"></i> <!-- /ko -->
  199. </a>
  200. <div class="center assist-spinner" data-bind="visible: loading() && open()"><i class="fa fa-spinner fa-spin"></i></div>
  201. <!-- ko template: { if: open, name: 'assist-db-entries' } --><!-- /ko -->
  202. </li>
  203. </script>
  204. <script type="text/html" id="assist-column-entry">
  205. <li data-bind="appAwareTemplateContextMenu: { template: 'sql-context-items', scrollContainer: '.assist-db-scrollable' }, visible: ! hasErrors(), visibleOnHover: { childrenOnly: true, override: statsVisible, selector: catalogEntry.isView() ? '.table-actions' : '.column-actions' }, css: { 'assist-table': catalogEntry.isView(), 'assist-column': catalogEntry.isField() }">
  206. <div class="assist-actions column-actions" data-bind="css: { 'assist-actions-left': navigationSettings.rightAssist }" style="opacity: 0">
  207. <a class="inactive-action" href="javascript:void(0)" data-bind="visible: navigationSettings.showStats, click: showContextPopover, css: { 'blue': statsVisible }"><i class="fa fa-fw fa-info" title="${_('Show details')}"></i></a>
  208. </div>
  209. <!-- ko if: expandable -->
  210. <a class="assist-entry assist-field-link" href="javascript:void(0)" data-bind="click: toggleOpen, attr: {'title': catalogEntry.getTitle() }, css: { 'assist-entry-left-action': navigationSettings.rightAssist }">
  211. <span class="highlightable" data-bind="css: { 'highlight': highlight}, attr: {'column': columnName, 'table': tableName, 'database': databaseName }, text: catalogEntry.getDisplayName(), draggableText: { text: editorText, meta: {'type': 'sql', 'column': columnName, 'table': tableName, 'database': databaseName } }"></span><!-- ko if: catalogEntry.isPrimaryKey() --> <i class="fa fa-key"></i><!-- /ko -->
  212. </a>
  213. <!-- /ko -->
  214. <!-- ko ifnot: expandable -->
  215. <div class="assist-entry assist-field-link default-cursor" href="javascript:void(0)" data-bind="event: { dblclick: dblClick }, attr: {'title': catalogEntry.getTitle() }, css: { 'assist-entry-left-action': navigationSettings.rightAssist }">
  216. <span class="highlightable" data-bind="css: { 'highlight': highlight}, attr: {'column': columnName, 'table': tableName, 'database': databaseName}, text: catalogEntry.getDisplayName(), draggableText: { text: editorText, meta: {'type': 'sql', 'column': columnName, 'table': tableName, 'database': databaseName} }"></span><!-- ko if: catalogEntry.isPrimaryKey() --> <i class="fa fa-key"></i><!-- /ko --><!-- ko if: assistDbSource.activeSort() === 'popular' && popularity() > 0 --> <i title="${ _('Popular') }" class="fa fa-star-o top-star"></i> <!-- /ko -->
  217. </div>
  218. <!-- /ko -->
  219. <div class="center assist-spinner" data-bind="visible: loading"><i class="fa fa-spinner fa-spin"></i></div>
  220. <!-- ko template: { if: open, name: 'assist-db-entries' } --><!-- /ko -->
  221. </li>
  222. </script>
  223. <script type="text/html" id="assist-column-entry-assistant">
  224. <li data-bind="appAwareTemplateContextMenu: { template: 'sql-context-items', scrollContainer: '.assist-db-scrollable' }, visible: ! hasErrors(), visibleOnHover: { childrenOnly: true, override: statsVisible, selector: catalogEntry.isView() ? '.table-actions' : '.column-actions' }, css: { 'assist-table': catalogEntry.isView(), 'assist-column': catalogEntry.isField() }">
  225. <div class="assist-actions column-actions assist-actions-left" style="opacity: 0">
  226. <a class="inactive-action" href="javascript:void(0)" data-bind="visible: navigationSettings.showStats, click: showContextPopover, css: { 'blue': statsVisible }"><i class="fa fa-fw fa-info" title="${_('Show details')}"></i></a>
  227. </div>
  228. <!-- ko if: expandable -->
  229. <a class="assist-entry assist-field-link assist-field-link-dark assist-entry-left-action assist-ellipsis" href="javascript:void(0)" data-bind="click: toggleOpen, attr: {'title': catalogEntry.getTitle() }">
  230. <span data-bind="text: catalogEntry.getType()" class="muted pull-right margin-right-20"></span>
  231. <span class="highlightable" data-bind="css: { 'highlight': highlight}, attr: {'column': columnName, 'table': tableName, 'database': databaseName }, text: catalogEntry.name, draggableText: { text: editorText, meta: {'type': 'sql', 'column': columnName, 'table': tableName, 'database': databaseName } }"></span><!-- ko if: catalogEntry.isPrimaryKey() --> <i class="fa fa-key"></i><!-- /ko -->
  232. </a>
  233. <!-- /ko -->
  234. <!-- ko ifnot: expandable -->
  235. <div class="assist-entry assist-field-link assist-field-link-dark default-cursor assist-ellipsis" href="javascript:void(0)" data-bind="event: { dblclick: dblClick }, attr: {'title': catalogEntry.getTitle() }, css: { 'assist-entry-left-action': navigationSettings.rightAssist }">
  236. <span data-bind="text: catalogEntry.getType()" class="muted pull-right margin-right-20"></span>
  237. <span class="highlightable" data-bind="css: { 'highlight': highlight}, attr: {'column': columnName, 'table': tableName, 'database': databaseName}, text: catalogEntry.name, draggableText: { text: editorText, meta: {'type': 'sql', 'column': columnName, 'table': tableName, 'database': databaseName} }"></span><!-- ko if: catalogEntry.isPrimaryKey() --> <i class="fa fa-key"></i><!-- /ko --><!-- ko if: assistDbSource.activeSort() === 'popular' && popularity() > 0 --> <i title="${ _('Popular') }" class="fa fa-star-o top-star"></i> <!-- /ko -->
  238. </div>
  239. <!-- /ko -->
  240. <div class="center assist-spinner" data-bind="visible: loading"><i class="fa fa-spinner fa-spin"></i></div>
  241. <!-- ko template: { if: open, name: 'assist-db-entries' } --><!-- /ko -->
  242. </li>
  243. </script>
  244. <script type="text/html" id="assist-db-entries">
  245. <!-- ko if: ! hasErrors() && hasEntries() && ! loading() && filteredEntries().length == 0 -->
  246. <ul class="assist-tables">
  247. <li class="assist-entry assist-no-entries"><!-- ko if: catalogEntry.isTable() -->${_('No columns found')}<!--/ko--><!-- ko if: catalogEntry.isDatabase() -->${_('No tables found')}<!--/ko--><!-- ko if: catalogEntry.isField() -->${_('No results found')}<!--/ko--></li>
  248. </ul>
  249. <!-- /ko -->
  250. <!-- ko if: ! hasErrors() && hasEntries() && ! loading() && filteredEntries().length > 0 -->
  251. <ul class="database-tree" data-bind="foreachVisible: { data: filteredEntries, minHeight: navigationSettings.rightAssist ? 22 : 23, container: '.assist-db-scrollable', skipScrollEvent: navigationSettings.rightAssist }, css: { 'assist-tables': catalogEntry.isDatabase() }">
  252. <!-- ko template: { if: catalogEntry.isTableOrView(), name: 'assist-table-entry' } --><!-- /ko -->
  253. <!-- ko if: navigationSettings.rightAssist -->
  254. <!-- ko template: { ifnot: catalogEntry.isTableOrView(), name: 'assist-column-entry-assistant' } --><!-- /ko -->
  255. <!-- /ko -->
  256. <!-- ko ifnot: navigationSettings.rightAssist -->
  257. <!-- ko template: { ifnot: catalogEntry.isTableOrView(), name: 'assist-column-entry' } --><!-- /ko -->
  258. <!-- /ko -->
  259. </ul>
  260. <!-- /ko -->
  261. <!-- ko template: { if: ! hasErrors() && ! hasEntries() && ! loading() && (catalogEntry.isTableOrView()), name: 'assist-no-table-entries' } --><!-- /ko -->
  262. <!-- ko template: { if: ! hasErrors() && ! hasEntries() && ! loading() && catalogEntry.isDatabase(), name: 'assist-no-database-entries' } --><!-- /ko -->
  263. <!-- ko if: hasErrors -->
  264. <ul class="assist-tables">
  265. <!-- ko if: catalogEntry.isTableOrView() -->
  266. <li class="assist-errors">${ _('Error loading columns.') }</li>
  267. <!-- /ko -->
  268. <!-- ko if: catalogEntry.isField() -->
  269. <li class="assist-errors">${ _('Error loading fields.') }</li>
  270. <!-- /ko -->
  271. </ul>
  272. <!-- /ko -->
  273. </script>
  274. <script type="text/html" id="assist-db-breadcrumb">
  275. <div class="assist-flex-header assist-breadcrumb">
  276. <!-- ko if: selectedSource() && ! selectedSource().selectedDatabase() && sources().length === 1 -->
  277. <i class="fa fa-server assist-breadcrumb-text"></i>
  278. <span class="assist-breadcrumb-text" data-bind="text: breadcrumb, attr: {'title': breadcrumb }"></span>
  279. <!-- /ko -->
  280. <!-- ko if: selectedSource() && ! selectedSource().selectedDatabase() && sources().length > 1 -->
  281. <a data-bind="click: back">
  282. <i class="fa fa-chevron-left assist-breadcrumb-back"></i>
  283. <i class="fa fa-server assist-breadcrumb-text"></i>
  284. <span class="assist-breadcrumb-text" data-bind="text: breadcrumb, attr: {'title': breadcrumb }"></span>
  285. </a>
  286. <!-- /ko -->
  287. <!-- ko if: selectedSource() && selectedSource().selectedDatabase() -->
  288. <a data-bind="click: back, appAwareTemplateContextMenu: { template: 'sql-context-items', viewModel: selectedSource().selectedDatabase() }">
  289. <i class="fa fa-chevron-left assist-breadcrumb-back" ></i>
  290. <i class="fa fa-database assist-breadcrumb-text"></i>
  291. <span class="assist-breadcrumb-text" data-bind="text: breadcrumb, attr: {'title': breadcrumb }"></span>
  292. </a>
  293. <!-- /ko -->
  294. </div>
  295. </script>
  296. <script type="text/html" id="assist-sql-inner-panel">
  297. <!-- ko template: { if: breadcrumb() !== null, name: 'assist-db-breadcrumb' } --><!-- /ko -->
  298. <!-- ko template: { ifnot: selectedSource, name: 'assist-sources-template' } --><!-- /ko -->
  299. <!-- ko with: selectedSource -->
  300. <!-- ko template: { ifnot: selectedDatabase, name: 'assist-databases-template' }--><!-- /ko -->
  301. <!-- ko with: selectedDatabase -->
  302. <!-- ko template: { name: 'assist-tables-template' } --><!-- /ko -->
  303. <!-- /ko -->
  304. <!-- /ko -->
  305. </script>
  306. <script type="text/html" id="assist-solr-inner-panel">
  307. <!-- ko template: { ifnot: selectedSource, name: 'assist-sources-template' } --><!-- /ko -->
  308. <!-- ko with: selectedSource -->
  309. <!-- ko template: { ifnot: selectedDatabase, name: 'assist-databases-template' }--><!-- /ko -->
  310. <!-- ko with: selectedDatabase -->
  311. <!-- ko template: { name: 'assist-tables-template' } --><!-- /ko -->
  312. <!-- /ko -->
  313. <!-- /ko -->
  314. </script>
  315. <script type="text/html" id="s3-details-content">
  316. <!-- ko with: definition -->
  317. <div class="assist-details-wrap">
  318. <div><div class="assist-details-header">${ _('Size') }</div><div class="assist-details-value" data-bind="text: humansize"></div></div>
  319. <div><div class="assist-details-header">${ _('Permissions') }</div><div class="assist-details-value" data-bind="text: rwx"></div></div>
  320. </div>
  321. <!-- /ko -->
  322. </script>
  323. <script type="text/html" id="s3-details-title">
  324. <span data-bind="text: definition.name"></span>
  325. </script>
  326. <script type="text/html" id="assist-s3-header-actions">
  327. <div class="assist-db-header-actions">
  328. <a class="inactive-action" href="javascript:void(0)" data-bind="click: function () { huePubSub.publish('assist.s3.refresh'); }"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }" title="${_('Manual refresh')}"></i></a>
  329. </div>
  330. </script>
  331. <script type="text/html" id="assist-s3-inner-panel">
  332. <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
  333. <!-- ko with: selectedS3Entry -->
  334. <div class="assist-flex-header assist-breadcrumb" >
  335. <!-- ko if: parent !== null -->
  336. <a href="javascript: void(0);" data-bind="appAwareTemplateContextMenu: { template: 'hdfs-context-items', scrollContainer: '.assist-s3-scrollable' }, click: function () { huePubSub.publish('assist.selectS3Entry', parent); }">
  337. <i class="fa fa-fw fa-chevron-left"></i>
  338. <i class="fa fa-fw fa-folder-o"></i>
  339. <span data-bind="text: definition.name, tooltip: {'title': path, 'placement': 'top' }"></span>
  340. </a>
  341. <!-- /ko -->
  342. <!-- ko if: parent === null -->
  343. <div>
  344. <i class="fa fa-fw fa-folder-o"></i>
  345. <span data-bind="text: path"></span>
  346. </div>
  347. <!-- /ko -->
  348. <!-- ko template: 'assist-s3-header-actions' --><!-- /ko -->
  349. </div>
  350. <div class="assist-flex-search">
  351. <div class="assist-filter"><input class="clearable" type="text" placeholder="${ _('Filter...') }" data-bind="clearable: filter, value: filter, valueUpdate: 'afterkeydown'"/></div>
  352. </div>
  353. <div class="assist-flex-fill assist-s3-scrollable">
  354. <div data-bind="visible: ! loading() && ! hasErrors()" style="position: relative;">
  355. <!-- ko hueSpinner: { spin: loadingMore, overlay: true } --><!-- /ko -->
  356. <ul class="assist-tables" data-bind="foreachVisible: { data: entries, minHeight: 22, container: '.assist-s3-scrollable', fetchMore: $data.fetchMore.bind($data) }">
  357. <li class="assist-entry assist-table-link" style="position: relative;" data-bind="appAwareTemplateContextMenu: { template: 'hdfs-context-items', scrollContainer: '.assist-s3-scrollable' }, visibleOnHover: { 'selector': '.assist-actions' }">
  358. <div class="assist-actions table-actions" style="opacity: 0;" >
  359. <a style="padding: 0 3px;" class="inactive-action" href="javascript:void(0);" data-bind="templatePopover : { contentTemplate: 's3-details-content', titleTemplate: 's3-details-title', minWidth: '320px' }">
  360. <i class='fa fa-info' title="${ _('Details') }"></i>
  361. </a>
  362. </div>
  363. <a href="javascript:void(0)" class="assist-entry assist-table-link" data-bind="multiClick: { click: toggleOpen, dblClick: dblClick }, attr: {'title': definition.name }">
  364. <!-- ko if: definition.type === 'dir' -->
  365. <i class="fa fa-fw fa-folder-o muted valign-middle"></i>
  366. <!-- /ko -->
  367. <!-- ko if: definition.type === 'file' -->
  368. <i class="fa fa-fw fa-file-o muted valign-middle"></i>
  369. <!-- /ko -->
  370. <span draggable="true" data-bind="text: definition.name, draggableText: { text: '\'' + path + '\'', meta: {'type': 's3', 'definition': definition} }"></span>
  371. </a>
  372. </li>
  373. </ul>
  374. <!-- ko if: !loading() && entries().length === 0 -->
  375. <ul class="assist-tables">
  376. <li class="assist-entry"><span class="assist-no-entries"><!-- ko if: filter() -->${_('No results found')}<!-- /ko --><!-- ko ifnot: filter() -->${_('Empty directory')}<!-- /ko --></span></li>
  377. </ul>
  378. <!-- /ko -->
  379. </div>
  380. <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
  381. <div class="assist-errors" data-bind="visible: ! loading() && hasErrors()">
  382. <span>${ _('Error loading contents.') }</span>
  383. </div>
  384. </div>
  385. <!-- /ko -->
  386. </script>
  387. <script type="text/html" id="git-details-title">
  388. <span data-bind="text: definition.name"></span>
  389. </script>
  390. <script type="text/html" id="assist-git-header-actions">
  391. <div class="assist-db-header-actions">
  392. <a class="inactive-action" href="javascript:void(0)" data-bind="click: function () { huePubSub.publish('assist.git.refresh'); }"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }" title="${_('Manual refresh')}"></i></a>
  393. </div>
  394. </script>
  395. <script type="text/html" id="assist-git-inner-panel">
  396. <!-- ko with: selectedGitEntry -->
  397. <div class="assist-flex-header assist-breadcrumb" >
  398. <!-- ko if: parent !== null -->
  399. <a href="javascript: void(0);" data-bind="click: function () { huePubSub.publish('assist.selectGitEntry', parent); }">
  400. <i class="fa fa-fw fa-chevron-left"></i>
  401. <i class="fa fa-fw fa-folder-o"></i>
  402. <span data-bind="text: path, attr: {'title': path }"></span>
  403. </a>
  404. <!-- /ko -->
  405. <!-- ko if: parent === null -->
  406. <div>
  407. <i class="fa fa-fw fa-folder-o"></i>
  408. <span data-bind="text: path"></span>
  409. </div>
  410. <!-- /ko -->
  411. <!-- ko template: 'assist-git-header-actions' --><!-- /ko -->
  412. </div>
  413. <div class="assist-flex-fill assist-git-scrollable">
  414. <div data-bind="visible: ! loading() && ! hasErrors()" style="position: relative;">
  415. <!-- ko hueSpinner: { spin: loadingMore, overlay: true } --><!-- /ko -->
  416. <ul class="assist-tables" data-bind="foreachVisible: { data: entries, minHeight: 22, container: '.assist-git-scrollable' }">
  417. <li class="assist-entry assist-table-link" style="position: relative;" data-bind="visibleOnHover: { 'selector': '.assist-actions' }">
  418. <a href="javascript:void(0)" class="assist-entry assist-table-link" data-bind="multiClick: { click: toggleOpen, dblClick: dblClick }, attr: {'title': definition.name }">
  419. <!-- ko if: definition.type === 'dir' -->
  420. <i class="fa fa-fw fa-folder-o muted valign-middle"></i>
  421. <!-- /ko -->
  422. <!-- ko ifnot: definition.type === 'dir' -->
  423. <i class="fa fa-fw fa-file-o muted valign-middle"></i>
  424. <!-- /ko -->
  425. <span draggable="true" data-bind="text: definition.name, draggableText: { text: '\'' + path + '\'', meta: {'type': 'git', 'definition': definition} }"></span>
  426. </a>
  427. </li>
  428. </ul>
  429. <!-- ko if: !loading() && entries().length === 0 -->
  430. <ul class="assist-tables">
  431. <li class="assist-entry"><span class="assist-no-entries">${_('Empty directory')}</span></li>
  432. </ul>
  433. <!-- /ko -->
  434. </div>
  435. <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
  436. <div class="assist-errors" data-bind="visible: ! loading() && hasErrors()">
  437. <span>${ _('Error loading contents.') }</span>
  438. </div>
  439. </div>
  440. <!-- /ko -->
  441. </script>
  442. <script type="text/html" id="hdfs-details-content">
  443. <!-- ko with: definition -->
  444. <div class="assist-details-wrap">
  445. <div><div class="assist-details-header">${ _('Size') }</div><div class="assist-details-value" data-bind="text: humansize"></div></div>
  446. <!-- ko with: stats -->
  447. <div><div class="assist-details-header">${ _('User') }</div><div class="assist-details-value" data-bind="text: user"></div></div>
  448. <div><div class="assist-details-header">${ _('Group') }</div><div class="assist-details-value" data-bind="text: group"></div></div>
  449. <!-- /ko -->
  450. <div><div class="assist-details-header">${ _('Permissions') }</div><div class="assist-details-value" data-bind="text: rwx"></div></div>
  451. <div><div class="assist-details-header">${ _('Date') }</div><div class="assist-details-value" data-bind="text: mtime"></div></div>
  452. </div>
  453. <!-- /ko -->
  454. </script>
  455. <script type="text/html" id="hdfs-details-title">
  456. <span data-bind="text: definition.name"></span>
  457. </script>
  458. <script type="text/html" id="assist-hdfs-header-actions">
  459. <div class="assist-db-header-actions">
  460. <a class="inactive-action" href="javascript:void(0)" data-bind="click: goHome" title="Go to ${ home_dir }"><i class="pointer fa fa-home"></i></a>
  461. <a class="inactive-action" data-bind="dropzone: {
  462. url: '/filebrowser/upload/file?dest=' + path,
  463. params: { dest: path },
  464. paramName: 'hdfs_file',
  465. onError: function(x, e){ $(document).trigger('error', e); },
  466. onComplete: function () { huePubSub.publish('assist.hdfs.refresh'); huePubSub.publish('fb.hdfs.refresh', path); } }" title="${_('Upload file')}" href="javascript:void(0)">
  467. <div class="dz-message inline" data-dz-message><i class="pointer fa fa-plus" title="${_('Upload file')}"></i></div>
  468. </a>
  469. <a class="inactive-action" href="javascript:void(0)" data-bind="click: function () { huePubSub.publish('assist.hdfs.refresh'); }" title="${_('Manual refresh')}"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }"></i></a>
  470. </div>
  471. </script>
  472. <script type="text/html" id="assist-adls-header-actions">
  473. <div class="assist-db-header-actions">
  474. <a class="inactive-action" href="javascript:void(0)" data-bind="click: goHome" title="Go to ${ home_dir }"><i class="pointer fa fa-home"></i></a>
  475. <a class="inactive-action" data-bind="dropzone: {
  476. url: '/filebrowser/upload/file?dest=adl:' + path,
  477. params: { dest: path },
  478. paramName: 'hdfs_file',
  479. onError: function(x, e){ $(document).trigger('error', e); },
  480. onComplete: function () { huePubSub.publish('assist.adls.refresh'); } }" title="${_('Upload file')}" href="javascript:void(0)">
  481. <div class="dz-message inline" data-dz-message><i class="pointer fa fa-plus" title="${_('Upload file')}"></i></div>
  482. </a>
  483. <a class="inactive-action" href="javascript:void(0)" data-bind="click: function () { huePubSub.publish('assist.adls.refresh'); }" title="${_('Manual refresh')}"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }"></i></a>
  484. </div>
  485. </script>
  486. <script type="text/html" id="assist-hdfs-inner-panel">
  487. <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
  488. <!-- ko with: selectedHdfsEntry -->
  489. <div class="assist-flex-header assist-breadcrumb" >
  490. <!-- ko if: parent !== null -->
  491. <a href="javascript: void(0);" data-bind="appAwareTemplateContextMenu: { template: 'hdfs-context-items', scrollContainer: '.assist-hdfs-scrollable' }, click: function () { huePubSub.publish('assist.selectHdfsEntry', parent); }">
  492. <i class="fa fa-fw fa-chevron-left"></i>
  493. <i class="fa fa-fw fa-folder-o"></i>
  494. <span data-bind="text: definition.name, tooltip: {'title': path, 'placement': 'top' }"></span>
  495. </a>
  496. <!-- /ko -->
  497. <!-- ko if: parent === null -->
  498. <div>
  499. <i class="fa fa-fw fa-folder-o"></i>
  500. <span data-bind="text: path"></span>
  501. </div>
  502. <!-- /ko -->
  503. <!-- ko template: 'assist-hdfs-header-actions' --><!-- /ko -->
  504. </div>
  505. <div class="assist-flex-search">
  506. <div class="assist-filter"><input class="clearable" type="text" placeholder="${ _('Filter...') }" data-bind="clearable: filter, value: filter, valueUpdate: 'afterkeydown'"/></div>
  507. </div>
  508. <div class="assist-flex-fill assist-hdfs-scrollable">
  509. <div data-bind="visible: ! loading() && ! hasErrors()" style="position: relative;">
  510. <!-- ko hueSpinner: { spin: loadingMore, overlay: true } --><!-- /ko -->
  511. <ul class="assist-tables" data-bind="foreachVisible: { data: entries, minHeight: 22, container: '.assist-hdfs-scrollable', fetchMore: $data.fetchMore.bind($data) }">
  512. <li class="assist-entry assist-table-link" style="position: relative;" data-bind="appAwareTemplateContextMenu: { template: 'hdfs-context-items', scrollContainer: '.assist-hdfs-scrollable' }, visibleOnHover: { 'selector': '.assist-actions' }">
  513. <div class="assist-actions table-actions" style="opacity: 0;" >
  514. <a style="padding: 0 3px;" class="inactive-action" href="javascript:void(0);" data-bind="templatePopover : { contentTemplate: 'hdfs-details-content', titleTemplate: 'hdfs-details-title', minWidth: '320px' }">
  515. <i class='fa fa-info' title="${ _('Details') }"></i>
  516. </a>
  517. </div>
  518. <a href="javascript:void(0)" class="assist-entry assist-table-link" data-bind="multiClick: { click: toggleOpen, dblClick: dblClick }, attr: {'title': definition.name }">
  519. <!-- ko if: definition.type === 'dir' -->
  520. <i class="fa fa-fw fa-folder-o muted valign-middle"></i>
  521. <!-- /ko -->
  522. <!-- ko if: definition.type === 'file' -->
  523. <i class="fa fa-fw fa-file-o muted valign-middle"></i>
  524. <!-- /ko -->
  525. <span draggable="true" data-bind="text: definition.name, draggableText: { text: '\'' + path + '\'', meta: {'type': 'hdfs', 'definition': definition} }"></span>
  526. </a>
  527. </li>
  528. </ul>
  529. <!-- ko if: !loading() && entries().length === 0 -->
  530. <ul class="assist-tables">
  531. <li class="assist-entry"><span class="assist-no-entries"><!-- ko if: filter() -->${_('No results found')}<!-- /ko --><!-- ko ifnot: filter() -->${_('Empty directory')}<!-- /ko --></span></li>
  532. </ul>
  533. <!-- /ko -->
  534. </div>
  535. <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
  536. <div class="assist-errors" data-bind="visible: ! loading() && hasErrors()">
  537. <span>${ _('Error loading contents.') }</span>
  538. </div>
  539. </div>
  540. <!-- /ko -->
  541. </script>
  542. <script type="text/html" id="assist-adls-inner-panel">
  543. <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
  544. <!-- ko with: selectedAdlsEntry -->
  545. <div class="assist-flex-header assist-breadcrumb" >
  546. <!-- ko if: parent !== null -->
  547. <a href="javascript: void(0);" data-bind="appAwareTemplateContextMenu: { template: 'hdfs-context-items', scrollContainer: '.assist-adls-scrollable' }, click: function () { huePubSub.publish('assist.selectAdlsEntry', parent); }">
  548. <i class="fa fa-fw fa-chevron-left"></i>
  549. <i class="fa fa-fw fa-folder-o"></i>
  550. <span data-bind="text: definition.name, tooltip: {'title': path, 'placement': 'top' }"></span>
  551. </a>
  552. <!-- /ko -->
  553. <!-- ko if: parent === null -->
  554. <div>
  555. <i class="fa fa-fw fa-folder-o"></i>
  556. <span data-bind="text: path"></span>
  557. </div>
  558. <!-- /ko -->
  559. <!-- ko template: 'assist-adls-header-actions' --><!-- /ko -->
  560. </div>
  561. <div class="assist-flex-search">
  562. <div class="assist-filter"><input class="clearable" type="text" placeholder="${ _('Filter...') }" data-bind="clearable: filter, value: filter, valueUpdate: 'afterkeydown'"/></div>
  563. </div>
  564. <div class="assist-flex-fill assist-adls-scrollable">
  565. <div data-bind="visible: ! loading() && ! hasErrors()" style="position: relative;">
  566. <!-- ko hueSpinner: { spin: loadingMore, overlay: true } --><!-- /ko -->
  567. <ul class="assist-tables" data-bind="foreachVisible: { data: entries, minHeight: 22, container: '.assist-adls-scrollable', fetchMore: $data.fetchMore.bind($data) }">
  568. <li class="assist-entry assist-table-link" style="position: relative;" data-bind="appAwareTemplateContextMenu: { template: 'hdfs-context-items', scrollContainer: '.assist-adls-scrollable' }, visibleOnHover: { 'selector': '.assist-actions' }">
  569. <div class="assist-actions table-actions" style="opacity: 0;" >
  570. <a style="padding: 0 3px;" class="inactive-action" href="javascript:void(0);" data-bind="templatePopover : { contentTemplate: 'hdfs-details-content', titleTemplate: 'hdfs-details-title', minWidth: '320px' }">
  571. <i class='fa fa-info' title="${ _('Details') }"></i>
  572. </a>
  573. </div>
  574. <a href="javascript:void(0)" class="assist-entry assist-table-link" data-bind="multiClick: { click: toggleOpen, dblClick: dblClick }, attr: {'title': definition.name }">
  575. <!-- ko if: definition.type === 'dir' -->
  576. <i class="fa fa-fw fa-folder-o muted valign-middle"></i>
  577. <!-- /ko -->
  578. <!-- ko if: definition.type === 'file' -->
  579. <i class="fa fa-fw fa-file-o muted valign-middle"></i>
  580. <!-- /ko -->
  581. <span draggable="true" data-bind="text: definition.name, draggableText: { text: '\'' + path + '\'', meta: {'type': 'adls', 'definition': definition} }"></span>
  582. </a>
  583. </li>
  584. </ul>
  585. <!-- ko if: !loading() && entries().length === 0 -->
  586. <ul class="assist-tables">
  587. <li class="assist-entry"><span class="assist-no-entries"><!-- ko if: filter() -->${_('No results found')}<!-- /ko --><!-- ko ifnot: filter() -->${_('Empty directory')}<!-- /ko --></span></li>
  588. </ul>
  589. <!-- /ko -->
  590. </div>
  591. <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
  592. <div class="assist-errors" data-bind="visible: ! loading() && hasErrors()">
  593. <span>${ _('Error loading contents.') }</span>
  594. </div>
  595. </div>
  596. <!-- /ko -->
  597. </script>
  598. <script type="text/html" id="assist-document-header-actions">
  599. <div class="assist-db-header-actions">
  600. <!-- ko if: !loading() -->
  601. <div class="highlightable" data-bind="css: { 'highlight': $parent.highlightTypeFilter() }, component: { name: 'hue-drop-down', params: { fixedPosition: true, value: typeFilter, searchable: true, entries: DOCUMENT_TYPES, linkTitle: '${ _ko('Document type') }' } }" style="display: inline-block"></div>
  602. <!-- /ko -->
  603. <a class="inactive-action" href="javascript:void(0)" data-bind="click: function () { huePubSub.publish('assist.document.refresh'); }"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }" title="${_('Manual refresh')}"></i></a>
  604. </div>
  605. </script>
  606. <script type="text/html" id="assist-documents-inner-panel">
  607. <!-- ko with: activeEntry -->
  608. <div class="assist-flex-header assist-breadcrumb">
  609. <!-- ko ifnot: isRoot -->
  610. <a href="javascript: void(0);" data-bind="click: function () { if (loaded()) { parent.makeActive(); } }">
  611. <i class="fa fa-fw fa-chevron-left"></i>
  612. <i class="fa fa-fw fa-folder-o"></i>
  613. <span data-bind="text: definition().name, attr: {'title': definition().name }"></span>
  614. </a>
  615. <!-- /ko -->
  616. <!-- ko if: isRoot -->
  617. <div>
  618. <i class="fa fa-fw fa-folder-o"></i>
  619. <span>/</span>
  620. </div>
  621. <!-- /ko -->
  622. <!-- ko template: 'assist-document-header-actions' --><!-- /ko -->
  623. </div>
  624. <div class="assist-flex-search">
  625. <div class="assist-filter"><input class="clearable" type="text" placeholder="${ _('Filter...') }" data-bind="clearable: filter, value: filter, valueUpdate: 'afterkeydown'"/></div>
  626. </div>
  627. <div class="assist-flex-fill assist-file-scrollable">
  628. <div data-bind="visible: ! loading() && ! hasErrors() && entries().length > 0">
  629. <!-- ko if: filteredEntries().length == 0 -->
  630. <ul class="assist-tables">
  631. <li class="assist-entry"><span class="assist-no-entries">${_('No documents found')}</span></li>
  632. </ul>
  633. <!-- /ko -->
  634. <ul class="assist-tables" data-bind="foreachVisible: { data: filteredEntries, minHeight: 27, container: '.assist-file-scrollable' }">
  635. <li class="assist-entry assist-file-entry" data-bind="appAwareTemplateContextMenu: { template: 'document-context-items', scrollContainer: '.assist-file-scrollable' }, assistFileDroppable, assistFileDraggable, visibleOnHover: { 'selector': '.assist-file-actions' }">
  636. <div class="assist-file-actions table-actions">
  637. <a class="inactive-action" href="javascript:void(0)" data-bind="click: showContextPopover, css: { 'blue': statsVisible }"><i class="fa fa-fw fa-info" title="${_('Show details')}"></i></a>
  638. </div>
  639. <a href="javascript:void(0)" class="assist-entry assist-document-link" data-bind="click: open, attr: {'title': name }">
  640. <!-- ko template: { name: 'document-icon-template', data: { document: $data, showShareAddon: false } } --><!-- /ko -->
  641. <span class="highlightable" data-bind="css: { 'highlight': highlight }, text: definition().name"></span>
  642. </a>
  643. </li>
  644. </ul>
  645. </div>
  646. <div data-bind="visible: !loading() && ! hasErrors() && entries().length === 0">
  647. <span class="assist-no-entries">${_('Empty directory')}</span>
  648. </div>
  649. <div class="center" data-bind="visible: loading() && ! hasErrors()">
  650. <i class="fa fa-spinner fa-spin" style="font-size: 20px; color: #BBB"></i>
  651. </div>
  652. <div class="assist-errors" data-bind="visible: ! loading() && hasErrors()">
  653. <span>${ _('Error loading contents.') }</span>
  654. </div>
  655. <!-- /ko -->
  656. </div>
  657. </script>
  658. <script type="text/html" id="assist-hbase-header-actions">
  659. <div class="assist-db-header-actions">
  660. <a class="inactive-action" href="javascript:void(0)" data-bind="click: function () { huePubSub.publish('assist.hbase.refresh'); }"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }" title="${_('Manual refresh')}"></i></a>
  661. </div>
  662. </script>
  663. <script type="text/html" id="assist-hbase-inner-panel">
  664. <!-- ko with: selectedHBaseEntry -->
  665. <div class="assist-inner-header assist-breadcrumb" >
  666. <!-- ko if: definition.host !== '' -->
  667. <a href="javascript: void(0);" data-bind="click: function () { huePubSub.publish('assist.clickHBaseRootItem'); }">
  668. <i class="fa fa-fw fa-chevron-left"></i>
  669. <i class="fa fa-fw fa-th-large"></i>
  670. <span data-bind="text: definition.name"></span>
  671. </a>
  672. <!-- /ko -->
  673. <!-- ko if: definition.host === '' -->
  674. ${_('Clusters')}
  675. <!-- /ko -->
  676. <!-- ko template: 'assist-hbase-header-actions' --><!-- /ko -->
  677. </div>
  678. <div class="assist-flex-fill assist-hbase-scrollable">
  679. <div data-bind="visible: ! loading() && ! hasErrors()" style="position: relative;">
  680. <ul class="assist-tables" data-bind="foreachVisible: { data: entries, minHeight: 22, container: '.assist-hbase-scrollable' }">
  681. <li class="assist-entry assist-table-link" style="position: relative;" data-bind="appAwareTemplateContextMenu: { template: 'hbase-context-items', scrollContainer: '.assist-hbase-scrollable' }, visibleOnHover: { 'selector': '.assist-actions' }">
  682. <a href="javascript:void(0)" class="assist-entry assist-table-link" data-bind="multiClick: { click: click, dblClick: dblClick }, attr: {'title': definition.name }">
  683. <!-- ko if: definition.host -->
  684. <i class="fa fa-fw fa-th-large muted valign-middle"></i>
  685. <!-- /ko -->
  686. <!-- ko ifnot: definition.host -->
  687. <i class="fa fa-fw fa-th muted valign-middle"></i>
  688. <!-- /ko -->
  689. <span draggable="true" data-bind="text: definition.name, draggableText: { text: '\'' + definition.name + '\'', meta: {'type': 'collection', 'definition': definition} }"></span>
  690. </a>
  691. </li>
  692. </ul>
  693. <!-- ko if: !loading() && entries().length === 0 -->
  694. <ul class="assist-tables">
  695. <li class="assist-entry">
  696. <span class="assist-no-entries">
  697. <!-- ko if: definition.host === '' -->
  698. ${_('No clusters available.')}
  699. <!-- /ko -->
  700. <!-- ko if: definition.host !== '' -->
  701. ${_('No tables available.')}
  702. <!-- /ko -->
  703. </span>
  704. </li>
  705. </ul>
  706. <!-- /ko -->
  707. </div>
  708. <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
  709. <div class="assist-errors" data-bind="visible: ! loading() && hasErrors()">
  710. <span>${ _('Error loading contents.') }</span>
  711. </div>
  712. </div>
  713. <!-- /ko -->
  714. </script>
  715. <script type="text/html" id="assist-sources-template">
  716. <div class="assist-flex-header">
  717. <div class="assist-inner-header">
  718. ${_('Sources')}
  719. </div>
  720. </div>
  721. <div class="assist-flex-fill">
  722. <ul class="assist-tables" data-bind="foreach: sources">
  723. <li class="assist-table">
  724. <a class="assist-table-link" href="javascript: void(0);" data-bind="click: function () { $parent.selectedSource($data); }"><i class="fa fa-fw fa-server muted valign-middle"></i> <span data-bind="text: name"></span></a>
  725. </li>
  726. </ul>
  727. </div>
  728. </script>
  729. <script type="text/html" id="ask-for-invalidate-title">
  730. &nbsp;<a class="pull-right pointer close-popover inactive-action">&times;</a>
  731. </script>
  732. <script type="text/html" id="ask-for-invalidate-content">
  733. <label class="radio">
  734. <input type="radio" name="refreshImpala" value="cache" data-bind="checked: invalidateOnRefresh" />
  735. ${ _('Clear cache') }
  736. </label>
  737. <label class="radio">
  738. <input type="radio" name="refreshImpala" value="invalidate" data-bind="checked: invalidateOnRefresh" />
  739. ${ _('Perform incremental metadata update') }
  740. </label>
  741. <div class="assist-invalidate-description">${ _('This will sync missing tables in Hive.') }</div>
  742. <label class="radio">
  743. <input type="radio" name="refreshImpala" value="invalidateAndFlush" data-bind="checked: invalidateOnRefresh" />
  744. ${ _('Invalidate all metadata and rebuild index') }
  745. </label>
  746. <div class="assist-invalidate-description">${ _('WARNING: This can be both resource and time-intensive.') }</div>
  747. <div style="width: 100%; display: inline-block; margin-top: 5px;"><button class="pull-right btn btn-primary" data-bind="css: { 'btn-primary': invalidateOnRefresh() !== 'invalidateAndFlush', 'btn-danger': invalidateOnRefresh() === 'invalidateAndFlush' }, click: function (data, event) { huePubSub.publish('close.popover'); triggerRefresh(data, event); }, clickBubble: false">${ _('Refresh') }</button></div>
  748. </script>
  749. <script type="text/html" id="assist-db-header-actions">
  750. <div class="assist-db-header-actions">
  751. <!-- ko ifnot: loading -->
  752. <span class="assist-tables-counter">(<span data-bind="text: filteredEntries().length"></span>)</span>
  753. <!-- ko if: (typeof isSource === 'undefined' || !isSource) && sourceType !== 'solr' -->
  754. <a class="inactive-action" href="javascript:void(0)" data-bind="click: toggleSearch, css: { 'blue' : isSearchVisible }"><i class="pointer fa fa-filter" title="${_('Filter')}"></i></a>
  755. <!-- /ko -->
  756. % if hasattr(ENABLE_NEW_CREATE_TABLE, 'get') and ENABLE_NEW_CREATE_TABLE.get():
  757. <!-- ko if: sourceType === 'hive' || sourceType === 'impala' -->
  758. <!-- ko if: typeof databaseName !== 'undefined' -->
  759. <a class="inactive-action" data-bind="hueLink: '${ url('indexer:importer_prefill', source_type='all', target_type='table') }' + databaseName + '/?sourceType=' + sourceType" title="${_('Create table')}" href="javascript:void(0)">
  760. <i class="pointer fa fa-plus" title="${_('Create table')}"></i>
  761. </a>
  762. <!-- /ko -->
  763. <!-- ko if: typeof databases !== 'undefined' -->
  764. <a class="inactive-action" data-bind="hueLink: '${ url('indexer:importer_prefill', source_type='manual', target_type='database') }' + '/?sourceType=' + sourceType" href="javascript:void(0)">
  765. <i class="pointer fa fa-plus" title="${ _('Create database') }"></i>
  766. </a>
  767. <!-- /ko -->
  768. <!-- /ko -->
  769. % endif
  770. <!-- ko if: sourceType === 'solr' -->
  771. <a class="inactive-action" data-bind="hueLink: '/indexer/importer/prefill/all/index/'" title="${_('Create index')}">
  772. <i class="pointer fa fa-plus" title="${_('Create index')}"></i>
  773. </a>
  774. <!-- /ko -->
  775. <!-- ko if: sourceType === 'impala' -->
  776. <a class="inactive-action" href="javascript:void(0)" data-bind="templatePopover : { contentTemplate: 'ask-for-invalidate-content', titleTemplate: 'ask-for-invalidate-title', trigger: 'click', minWidth: '320px' }"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }" title="${_('Refresh')}"></i></a>
  777. <!-- /ko -->
  778. <!-- ko if: sourceType !== 'impala' -->
  779. <a class="inactive-action" href="javascript:void(0)" data-bind="click: triggerRefresh"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }" title="${_('Refresh')}"></i></a>
  780. <!-- /ko -->
  781. <!-- /ko -->
  782. <!-- ko if: loading -->
  783. <i class="fa fa-refresh fa-spin blue" title="${_('Refresh')}"></i>
  784. <!-- /ko -->
  785. </div>
  786. </script>
  787. <script type="text/html" id="assist-databases-template">
  788. <div class="assist-flex-header" data-bind="visibleOnHover: { selector: '.hover-actions', override: loading() }">
  789. <div class="assist-inner-header">
  790. <!-- ko ifnot: sourceType === 'solr' -->
  791. ${_('Databases')}
  792. <!-- ko template: 'assist-db-header-actions' --><!-- /ko -->
  793. <!-- /ko -->
  794. <!-- ko if: sourceType === 'solr' -->
  795. ${_('Sources')}
  796. <!-- /ko -->
  797. </div>
  798. </div>
  799. <div class="assist-flex-search" data-bind="visible: hasEntries() && ! hasErrors()">
  800. <div class="assist-filter">
  801. <!-- ko component: {
  802. name: 'inline-autocomplete',
  803. params: {
  804. querySpec: filter.querySpec,
  805. facets: [],
  806. knownFacetValues: {},
  807. autocompleteFromEntries: autocompleteFromEntries
  808. }
  809. } --><!-- /ko -->
  810. </div>
  811. </div>
  812. <div class="assist-flex-fill assist-db-scrollable" data-bind="visible: ! hasErrors() && ! loading() && hasEntries()">
  813. <!-- ko if: ! loading() && filteredEntries().length == 0 -->
  814. <ul class="assist-tables">
  815. <li class="assist-entry no-entries">${_('No results found')}</li>
  816. </ul>
  817. <!-- /ko -->
  818. <ul class="assist-tables" data-bind="foreachVisible: {data: filteredEntries, minHeight: 23, container: '.assist-db-scrollable', skipScrollEvent: navigationSettings.rightAssist }">
  819. <!-- ko template: { name: 'assist-database-entry' } --><!-- /ko -->
  820. </ul>
  821. </div>
  822. <div class="assist-flex-fill" data-bind="visible: loading">
  823. <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
  824. </div>
  825. <div class="assist-flex-fill" data-bind="visible: hasErrors() && ! loading()">
  826. <span class="assist-errors">${ _('Error loading databases.') }</span>
  827. </div>
  828. <div class="assist-flex-fill" data-bind="visible: ! hasErrors() && ! loading() && ! hasEntries()">
  829. <span class="assist-errors">${ _('No databases found.') }</span>
  830. </div>
  831. </script>
  832. <script type="text/html" id="assist-tables-template">
  833. <div class="assist-flex-header">
  834. <div class="assist-inner-header" data-bind="visible: !$parent.loading() && !$parent.hasErrors()">
  835. <!-- ko ifnot: sourceType === 'solr' -->
  836. ${_('Tables')}
  837. <!-- /ko -->
  838. <!-- ko if: sourceType === 'solr' -->
  839. <div data-bind="appAwareTemplateContextMenu: { template: 'collection-title-context-items', scrollContainer: '.assist-db-scrollable' }">${_('Collections')}</div>
  840. <!-- /ko -->
  841. <!-- ko template: 'assist-db-header-actions' --><!-- /ko -->
  842. </div>
  843. </div>
  844. <div class="assist-flex-search" data-bind="visible: hasEntries() && isSearchVisible() && !$parent.loading() && !$parent.hasErrors()">
  845. <label class="checkbox inline-block margin-left-5"><input type="checkbox" data-bind="checked: filter.showTables" />${_('Tables')}</label>
  846. <label class="checkbox inline-block margin-left-5"><input type="checkbox" data-bind="checked: filter.showViews" />${_('Views')}</label>
  847. <!-- ko if: $parent.activeSort -->
  848. <a class="assist-sort inactive-action inactive-action-dark" style="position: absolute;" data-toggle="dropdown" href="javascript:void(0)">
  849. <!-- ko if: $parent.activeSort() === 'creation' -->
  850. <i class="pointer fa fa-sort" title="${_('Sort')}"></i>
  851. <!-- /ko -->
  852. <!-- ko if: $parent.activeSort() === 'popular' -->
  853. <i class="pointer fa fa-star-o" title="${_('Sort')}"></i>
  854. <!-- /ko -->
  855. <!-- ko if: $parent.activeSort() === 'alpha' -->
  856. <i class="pointer fa fa-sort-alpha-asc" title="${_('Sort')}"></i>
  857. <!-- /ko -->
  858. ${_('Sort')}
  859. </a>
  860. <ul class="dropdown-menu" style="top: initial; left: inherit; position: fixed; z-index:10000;">
  861. <li>
  862. <a href="javascript:void(0)" data-bind="click: function () { $parent.activeSort('creation'); }">
  863. <i class="fa fa-fw" data-bind="css: { 'fa-check': $parent.activeSort() === 'creation' }"></i> ${ _('Default') }
  864. </a>
  865. </li>
  866. <li>
  867. <a href="javascript:void(0)" data-bind="click: function () { $parent.activeSort('alpha'); }">
  868. <i class="fa fa-fw" data-bind="css: { 'fa-check': $parent.activeSort() === 'alpha' }"></i> ${ _('Alphabetical') }
  869. </a>
  870. </li>
  871. <!-- ko if: HAS_OPTIMIZER -->
  872. <li>
  873. <a href="javascript:void(0)" data-bind="click: function () { $parent.activeSort('popular'); }">
  874. <i class="fa fa-fw" data-bind="css: { 'fa-check': $parent.activeSort() === 'popular' }"></i> ${ _('Popularity') }
  875. </a>
  876. </li>
  877. <!-- /ko -->
  878. </ul>
  879. <!-- /ko -->
  880. </div>
  881. <div class="assist-flex-search" data-bind="visible: hasEntries() && !$parent.loading() && !$parent.hasErrors()">
  882. <div class="assist-filter">
  883. <!-- ko component: {
  884. name: 'inline-autocomplete',
  885. params: {
  886. querySpec: filter.querySpec,
  887. facets: ['type'],
  888. knownFacetValues: sourceType === 'solr' ? SOLR_ASSIST_KNOWN_FACET_VALUES : SQL_ASSIST_KNOWN_FACET_VALUES,
  889. autocompleteFromEntries: autocompleteFromEntries
  890. }
  891. } --><!-- /ko -->
  892. </div>
  893. </div>
  894. <div class="assist-flex-fill assist-db-scrollable" data-bind="visible: ! hasErrors() && ! loading()">
  895. <!-- ko template: 'assist-db-entries' --><!-- /ko -->
  896. </div>
  897. <div class="assist-flex-fill" data-bind="visible: loading() || $parent.loading()">
  898. <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
  899. </div>
  900. <div class="assist-flex-fill" data-bind="visible: hasErrors() && ! loading()">
  901. <span class="assist-errors">${ _('Error loading tables.') }</span>
  902. </div>
  903. </script>
  904. <script type="text/html" id="assist-panel-inner-header">
  905. <div class="assist-header assist-fixed-height" data-bind="visibleOnHover: { selector: '.assist-header-actions' }, css: { 'assist-resizer': $index() > 0 }" style="display:none;">
  906. <span data-bind="text: $parent.name"></span>
  907. <div class="assist-header-actions">
  908. <div class="inactive-action" data-bind="click: function () { $parent.visible(false) }"><i class="fa fa-times"></i></div>
  909. </div>
  910. </div>
  911. </script>
  912. <script type="text/html" id="assist-panel-template">
  913. <div class="assist-panel">
  914. <!-- ko if: availablePanels().length > 1 -->
  915. <div class="assist-panel-switches">
  916. <!-- ko foreach: availablePanels -->
  917. <div class="inactive-action assist-type-switch" data-bind="click: function () { $parent.visiblePanel($data); }, css: { 'blue': $parent.visiblePanel() === $data }, style: { 'float': rightAlignIcon ? 'right' : 'left' }, attr: { 'title': name }">
  918. <!-- ko if: iconSvg --><span style="font-size:22px;"><svg class="hi"><use data-bind="attr: {'xlink:href': iconSvg }" xlink:href=''></use></svg></span><!-- /ko -->
  919. <!-- ko if: !iconSvg --><i class="fa fa-fw valign-middle" data-bind="css: icon"></i><!-- /ko -->
  920. </div>
  921. <!-- /ko -->
  922. </div>
  923. <!-- /ko -->
  924. <!-- ko with: visiblePanel -->
  925. <div class="assist-panel-contents" data-bind="style: { 'padding-top': $parent.availablePanels().length > 1 ? '10px' : '5px' }">
  926. <div class="assist-inner-panel">
  927. <div class="assist-flex-panel">
  928. <!-- ko template: { name: templateName, data: panelData } --><!-- /ko -->
  929. </div>
  930. </div>
  931. </div>
  932. <!-- /ko -->
  933. </div>
  934. </script>
  935. <div class="assist-file-entry-drag">
  936. <span class="drag-text"></span>
  937. </div>
  938. <script type="text/javascript">
  939. var SQL_ASSIST_KNOWN_FACET_VALUES = {
  940. 'type': {'array': -1, 'table': -1, 'view': -1, 'boolean': -1, 'bigint': -1, 'binary': -1, 'char': -1, 'date': -1, 'double': -1, 'decimal': -1, 'float': -1, 'int': -1, 'map': -1, 'real': -1, 'smallint': -1, 'string': -1, 'struct': -1, 'timestamp': -1, 'tinyint': -1, 'varchar': -1 }
  941. };
  942. var SOLR_ASSIST_KNOWN_FACET_VALUES = {
  943. 'type': {'date': -1, 'tdate': -1, 'timestamp': -1, 'pdate': -1, 'int': -1, 'tint': -1, 'pint': -1, 'long': -1, 'tlong': -1, 'plong': -1, 'float': -1, 'tfloat': -1, 'pfloat': -1, 'double': -1, 'tdouble': -1, 'pdouble': -1, 'currency': -1, 'smallint': -1, 'bigint': -1, 'tinyint': -1, 'SpatialRecursivePrefixTreeFieldType': -1 }
  944. };
  945. (function () {
  946. ko.bindingHandlers.assistFileDroppable = {
  947. init: function(element, valueAccessor, allBindings, boundEntry) {
  948. var dragData;
  949. var dragSub = huePubSub.subscribe('doc.browser.dragging', function (data) {
  950. dragData = data;
  951. });
  952. ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
  953. dragSub.remove();
  954. });
  955. var $element = $(element);
  956. if (boundEntry.isDirectory) {
  957. $element.droppable({
  958. drop: function () {
  959. if (dragData && !dragData.dragToSelect && boundEntry.isDirectory()) {
  960. boundEntry.moveHere(dragData.selectedEntries);
  961. dragData.originEntry.load();
  962. }
  963. $element.removeClass('assist-file-entry-drop');
  964. },
  965. over: function () {
  966. if (!$element.hasClass('assist-file-entry-drop') && dragData && !dragData.dragToSelect && boundEntry.isDirectory()) {
  967. $element.addClass('assist-file-entry-drop');
  968. }
  969. },
  970. out: function () {
  971. $element.removeClass('assist-file-entry-drop');
  972. }
  973. })
  974. }
  975. }
  976. };
  977. ko.bindingHandlers.assistFileDraggable = {
  978. init: function(element, valueAccessor, allBindings, boundEntry, bindingContext) {
  979. var $element = $(element);
  980. var dragStartY = -1;
  981. var dragStartX = -1;
  982. $element.draggable({
  983. start: function (event, ui) {
  984. var $container = $('.doc-browser-drag-container');
  985. boundEntry.selected(true);
  986. huePubSub.publish('doc.browser.dragging', {
  987. selectedEntries: [ boundEntry ],
  988. originEntry: boundEntry,
  989. dragToSelect: false
  990. });
  991. huePubSub.publish('doc.drag.to.select', false);
  992. dragStartX = event.clientX;
  993. dragStartY = event.clientY;
  994. var $helper = $('.assist-file-entry-drag').clone().show();
  995. $helper.find('.drag-text').text(boundEntry.definition().name);
  996. $helper.find('i').removeClass().addClass($element.find('.doc-browser-primary-col i').attr('class'));
  997. $helper.appendTo($container);
  998. },
  999. drag: function (event) {
  1000. },
  1001. stop: function (event) {
  1002. var elementAtStart = document.elementFromPoint(dragStartX, dragStartY);
  1003. var elementAtStop = document.elementFromPoint(event.clientX, event.clientY);
  1004. if (elementAtStart.nodeName === "A" && elementAtStop.nodeName === "A" && Math.sqrt((dragStartX-event.clientX)*(dragStartX-event.clientX) + (dragStartY-event.clientY)*(dragStartY-event.clientY)) < 8) {
  1005. $(elementAtStop).trigger('click');
  1006. }
  1007. boundEntry.selected(false);
  1008. },
  1009. helper: function (event) {
  1010. return $('<div>').addClass('doc-browser-drag-container');
  1011. },
  1012. appendTo: "body",
  1013. cursorAt: {
  1014. top: 0,
  1015. left: 0
  1016. }
  1017. });
  1018. }
  1019. };
  1020. /**
  1021. * @param {Object} options
  1022. * @param {ApiHelper} options.apiHelper
  1023. * @param {string} options.type
  1024. * @param {number} options.minHeight
  1025. * @param {string} options.icon
  1026. * @param {boolean} [options.rightAlignIcon] - Default false
  1027. * @param {boolean} options.visible
  1028. * @param {(AssistDbPanel|AssistHdfsPanel|AssistGitPanel|AssistDocumentsPanel|AssistS3Panel)} panelData
  1029. * @constructor
  1030. */
  1031. function AssistInnerPanel (options) {
  1032. var self = this;
  1033. self.minHeight = options.minHeight;
  1034. self.icon = options.icon;
  1035. self.type = options.type;
  1036. self.name = options.name;
  1037. self.panelData = options.panelData;
  1038. self.rightAlignIcon = !!options.rightAlignIcon;
  1039. self.iconSvg = options.iconSvg;
  1040. self.visible = ko.observable(options.visible || true);
  1041. options.apiHelper.withTotalStorage('assist', 'showingPanel_' + self.type, self.visible, false, options.visible);
  1042. self.templateName = 'assist-' + self.type + '-inner-panel';
  1043. var loadWhenVisible = function () {
  1044. if (! self.visible()) {
  1045. return;
  1046. }
  1047. if (self.type === 'documents' && !self.panelData.activeEntry().loaded()) {
  1048. self.panelData.activeEntry().load();
  1049. }
  1050. };
  1051. self.visible.subscribe(loadWhenVisible);
  1052. loadWhenVisible();
  1053. }
  1054. /**
  1055. * @param {Object} options
  1056. * @param {ApiHelper} options.apiHelper
  1057. * @param {Object} options.i18n
  1058. * @param {Object[]} options.sourceTypes - All the available SQL source types
  1059. * @param {string} options.sourceTypes[].name - Example: Hive SQL
  1060. * @param {string} options.sourceTypes[].type - Example: hive
  1061. * @param {string} [options.activeSourceType] - Example: hive
  1062. * @param {Object} options.navigationSettings - enable/disable the links
  1063. * @param {boolean} options.navigationSettings.openItem
  1064. * @param {boolean} options.navigationSettings.showStats
  1065. * @param {boolean} options.navigationSettings.pinEnabled
  1066. * @constructor
  1067. **/
  1068. function AssistDbPanel(options) {
  1069. var self = this;
  1070. self.options = options;
  1071. self.apiHelper = options.apiHelper;
  1072. self.i18n = options.i18n;
  1073. self.initialized = false;
  1074. if (typeof options.sourceTypes === 'undefined') {
  1075. options.sourceTypes = [];
  1076. if (options.isSolr) {
  1077. options.sourceTypes = [{
  1078. type: 'solr',
  1079. name: 'solr'
  1080. }];
  1081. } else {
  1082. % for interpreter in get_ordered_interpreters(request.user):
  1083. % if interpreter["is_sql"]:
  1084. options.sourceTypes.push({
  1085. type: '${ interpreter["type"] }',
  1086. name: '${ interpreter["name"] }'
  1087. });
  1088. % endif
  1089. % endfor
  1090. }
  1091. }
  1092. self.sources = ko.observableArray();
  1093. self.sourceIndex = {};
  1094. $.each(options.sourceTypes, function (idx, sourceType) {
  1095. self.sourceIndex[sourceType.type] = new AssistDbSource({
  1096. apiHelper: self.apiHelper,
  1097. i18n: self.i18n,
  1098. type: sourceType.type,
  1099. name: sourceType.name,
  1100. navigationSettings: options.navigationSettings
  1101. });
  1102. self.sources.push(self.sourceIndex[sourceType.type]);
  1103. });
  1104. huePubSub.subscribe('assist.collections.refresh', function() {
  1105. DataCatalog.getEntry({ sourceType: 'solr', path: [] }).done(function (entry) {
  1106. entry.clear('cache', true);
  1107. });
  1108. });
  1109. self.selectedSource = ko.observable(null);
  1110. self.setDatabaseWhenLoaded = function (databaseName) {
  1111. if (self.selectedSource().loaded()) {
  1112. self.selectedSource().setDatabase(databaseName);
  1113. } else {
  1114. var subscription = self.selectedSource().loaded.subscribe(function (newValue) {
  1115. if (newValue) {
  1116. self.selectedSource().setDatabase(databaseName);
  1117. subscription.dispose();
  1118. }
  1119. });
  1120. if (!self.selectedSource().loaded() && !self.selectedSource().loading()) {
  1121. self.selectedSource().initDatabases();
  1122. }
  1123. }
  1124. };
  1125. huePubSub.subscribe('assist.db.highlight', function (location) {
  1126. huePubSub.publish('left.assist.show');
  1127. if (location.sourceType === 'solr') {
  1128. huePubSub.publish('assist.show.solr');
  1129. }
  1130. else {
  1131. huePubSub.publish('assist.show.sql');
  1132. }
  1133. huePubSub.publish('context.popover.hide');
  1134. window.setTimeout(function () {
  1135. var foundSource;
  1136. $.each(self.sources(), function (idx, source) {
  1137. if (source.sourceType === location.sourceType) {
  1138. foundSource = source;
  1139. return false;
  1140. }
  1141. });
  1142. if (foundSource) {
  1143. var whenLoaded = function () {
  1144. if (self.selectedSource() !== foundSource) {
  1145. self.selectedSource(foundSource);
  1146. }
  1147. foundSource.highlightInside(location.path);
  1148. };
  1149. if (foundSource.hasEntries()) {
  1150. whenLoaded();
  1151. } else {
  1152. foundSource.initDatabases(whenLoaded);
  1153. }
  1154. }
  1155. }, 0);
  1156. });
  1157. if (!options.isSolr) {
  1158. huePubSub.subscribe('assist.set.database', function (databaseDef) {
  1159. if (!databaseDef.source || !self.sourceIndex[databaseDef.source]) {
  1160. return;
  1161. }
  1162. self.selectedSource(self.sourceIndex[databaseDef.source]);
  1163. self.setDatabaseWhenLoaded(databaseDef.name);
  1164. });
  1165. huePubSub.subscribe('assist.get.database', function (source) {
  1166. if (self.sourceIndex[source] && self.sourceIndex[source].selectedDatabase()) {
  1167. huePubSub.publish('assist.database.set', {
  1168. source: source,
  1169. name: self.sourceIndex[source].selectedDatabase().databaseName
  1170. });
  1171. } else {
  1172. huePubSub.publish('assist.database.set', {
  1173. source: source,
  1174. name: 'default'
  1175. });
  1176. }
  1177. });
  1178. huePubSub.subscribe('assist.get.database.callback', function (options) {
  1179. if (self.sourceIndex[options.source] && self.sourceIndex[options.source].selectedDatabase()) {
  1180. options.callback({
  1181. source: options.source,
  1182. name: self.sourceIndex[options.source].selectedDatabase().databaseName
  1183. });
  1184. } else {
  1185. options.callback({
  1186. source: options.source,
  1187. name: 'default'
  1188. });
  1189. }
  1190. });
  1191. huePubSub.subscribe('assist.get.source', function () {
  1192. huePubSub.publish('assist.source.set', self.selectedSource() ? self.selectedSource().sourceType : undefined);
  1193. });
  1194. huePubSub.subscribe('assist.set.source', function (source) {
  1195. if (self.sourceIndex[source]) {
  1196. self.selectedSource(self.sourceIndex[source]);
  1197. }
  1198. });
  1199. huePubSub.publish('assist.db.panel.ready');
  1200. huePubSub.subscribe('assist.is.db.panel.ready', function () {
  1201. huePubSub.publish('assist.db.panel.ready');
  1202. });
  1203. self.selectedSource.subscribe(function (newSource) {
  1204. if (newSource) {
  1205. if (newSource.databases().length === 0) {
  1206. newSource.initDatabases();
  1207. }
  1208. self.apiHelper.setInTotalStorage('assist', 'lastSelectedSource', newSource.sourceType);
  1209. } else {
  1210. self.apiHelper.setInTotalStorage('assist', 'lastSelectedSource');
  1211. }
  1212. });
  1213. }
  1214. self.breadcrumb = ko.computed(function () {
  1215. if (self.selectedSource()) {
  1216. if (self.selectedSource().selectedDatabase()) {
  1217. return self.selectedSource().selectedDatabase().catalogEntry.name;
  1218. }
  1219. return self.selectedSource().name;
  1220. }
  1221. return null;
  1222. });
  1223. }
  1224. AssistDbPanel.prototype.back = function () {
  1225. var self = this;
  1226. if (self.selectedSource() && self.selectedSource().selectedDatabase()) {
  1227. self.selectedSource().selectedDatabase(null)
  1228. } else if (self.selectedSource()) {
  1229. self.selectedSource(null);
  1230. }
  1231. };
  1232. AssistDbPanel.prototype.init = function () {
  1233. var self = this;
  1234. if (self.initialized) {
  1235. return;
  1236. }
  1237. if (self.options.isSolr) {
  1238. self.selectedSource(self.sourceIndex['solr']);
  1239. self.setDatabaseWhenLoaded();
  1240. } else {
  1241. var storageSourceType = self.apiHelper.getFromTotalStorage('assist', 'lastSelectedSource');
  1242. if (!self.selectedSource()) {
  1243. if (self.options.activeSourceType) {
  1244. self.selectedSource(self.sourceIndex[self.options.activeSourceType]);
  1245. self.setDatabaseWhenLoaded();
  1246. } else if (storageSourceType && self.sourceIndex[storageSourceType]) {
  1247. self.selectedSource(self.sourceIndex[storageSourceType]);
  1248. self.setDatabaseWhenLoaded();
  1249. }
  1250. }
  1251. }
  1252. self.initialized = true;
  1253. };
  1254. /**
  1255. * @param {Object} options
  1256. * @param {ApiHelper} options.apiHelper
  1257. * @param {string} options.user
  1258. * @constructor
  1259. **/
  1260. function AssistDocumentsPanel (options) {
  1261. var self = this;
  1262. self.apiHelper = options.apiHelper;
  1263. self.user = options.user;
  1264. self.activeEntry = ko.observable();
  1265. self.activeSort = ko.observable('defaultAsc');
  1266. self.typeFilter = ko.observable(DOCUMENT_TYPES[0]); // all is first
  1267. self.highlightTypeFilter = ko.observable(false);
  1268. var lastOpenedUuid = self.apiHelper.getFromTotalStorage('assist', 'last.opened.assist.doc.uuid');
  1269. if (lastOpenedUuid) {
  1270. self.activeEntry(new HueFileEntry({
  1271. activeEntry: self.activeEntry,
  1272. trashEntry: ko.observable(),
  1273. apiHelper: self.apiHelper,
  1274. app: 'documents',
  1275. user: self.user,
  1276. activeSort: self.activeSort,
  1277. typeFilter: self.typeFilter,
  1278. definition: {
  1279. uuid: lastOpenedUuid,
  1280. type: 'directory'
  1281. }
  1282. }))
  1283. } else {
  1284. self.fallbackToRoot();
  1285. }
  1286. self.activeEntry.subscribe(function (newEntry) {
  1287. if (!newEntry.loaded()) {
  1288. var loadedSub = newEntry.loaded.subscribe(function (loaded) {
  1289. if (loaded && !newEntry.hasErrors() && newEntry.definition() && newEntry.definition().uuid) {
  1290. self.apiHelper.setInTotalStorage('assist', 'last.opened.assist.doc.uuid', newEntry.definition().uuid);
  1291. }
  1292. loadedSub.dispose();
  1293. })
  1294. } else if (!newEntry.hasErrors() && newEntry.definition() && newEntry.definition().uuid) {
  1295. self.apiHelper.setInTotalStorage('assist', 'last.opened.assist.doc.uuid', newEntry.definition().uuid);
  1296. }
  1297. });
  1298. self.reload = function () {
  1299. self.activeEntry().load(function () {}, function () {
  1300. self.fallbackToRoot();
  1301. });
  1302. };
  1303. huePubSub.subscribe('assist.document.refresh', function () {
  1304. huePubSub.publish('assist.clear.document.cache');
  1305. self.reload();
  1306. });
  1307. huePubSub.subscribe('assist.doc.highlight', function (details) {
  1308. huePubSub.publish('assist.show.documents');
  1309. huePubSub.publish('context.popover.hide');
  1310. var whenLoaded = $.Deferred().done(function () {
  1311. self.activeEntry().highlightInside(details.docUuid);
  1312. });
  1313. if (self.activeEntry() && self.activeEntry().definition() && self.activeEntry().definition().uuid === details.parentUuid) {
  1314. if (self.activeEntry().loaded() && !self.activeEntry().hasErrors()) {
  1315. whenLoaded.resolve();
  1316. } else {
  1317. var loadedSub = self.activeEntry().loaded.subscribe(function (newVal) {
  1318. if (newVal) {
  1319. if (!self.activeEntry().hasErrors()) {
  1320. whenLoaded.resolve();
  1321. }
  1322. whenLoaded.reject();
  1323. loadedSub.remove();
  1324. }
  1325. })
  1326. }
  1327. self.activeEntry().highlight(details.docUuid);
  1328. } else {
  1329. self.activeEntry(new HueFileEntry({
  1330. activeEntry: self.activeEntry,
  1331. trashEntry: ko.observable(),
  1332. apiHelper: self.apiHelper,
  1333. app: 'documents',
  1334. user: self.user,
  1335. activeSort: self.activeSort,
  1336. typeFilter: self.typeFilter,
  1337. definition: {
  1338. uuid: details.parentUuid,
  1339. type: 'directory'
  1340. }
  1341. }));
  1342. self.activeEntry().load(function() {
  1343. whenLoaded.resolve();
  1344. }, function () {
  1345. whenLoaded.reject();
  1346. self.fallbackToRoot();
  1347. });
  1348. }
  1349. });
  1350. }
  1351. AssistDocumentsPanel.prototype.setTypeFilter = function (newType) {
  1352. var self = this;
  1353. DOCUMENT_TYPES.some(function (docType) {
  1354. if (docType.type === newType) {
  1355. self.typeFilter(docType);
  1356. return true;
  1357. }
  1358. });
  1359. self.highlightTypeFilter(true);
  1360. window.setTimeout(function () {
  1361. self.highlightTypeFilter(false);
  1362. }, 600)
  1363. };
  1364. AssistDocumentsPanel.prototype.fallbackToRoot = function () {
  1365. var self = this;
  1366. if (!self.activeEntry() || self.activeEntry().definition() && (self.activeEntry().definition().path !== '/' || self.activeEntry().definition().uuid)) {
  1367. self.apiHelper.setInTotalStorage('assist', 'last.opened.assist.doc.uuid', null);
  1368. self.activeEntry(new HueFileEntry({
  1369. activeEntry: self.activeEntry,
  1370. trashEntry: ko.observable(),
  1371. apiHelper: self.apiHelper,
  1372. app: 'documents',
  1373. user: self.user,
  1374. activeSort: self.activeSort,
  1375. typeFilter: self.typeFilter,
  1376. definition: {
  1377. name: '/',
  1378. type: 'directory'
  1379. }
  1380. }));
  1381. self.activeEntry().load();
  1382. }
  1383. };
  1384. AssistDocumentsPanel.prototype.init = function () {
  1385. var self = this;
  1386. if (! self.activeEntry().loaded()) {
  1387. self.activeEntry().load(function () {}, function () {
  1388. self.fallbackToRoot();
  1389. }, true);
  1390. }
  1391. };
  1392. /**
  1393. * @param {Object} options
  1394. * @param {ApiHelper} options.apiHelper
  1395. * @constructor
  1396. **/
  1397. function AssistHdfsPanel (options) {
  1398. var self = this;
  1399. self.apiHelper = options.apiHelper;
  1400. self.selectedHdfsEntry = ko.observable();
  1401. self.loading = ko.observable();
  1402. self.initialized = false;
  1403. var loadPath = function (path) {
  1404. self.loading(true);
  1405. var parts = path.split('/');
  1406. parts.shift();
  1407. var currentEntry = new AssistStorageEntry({
  1408. type: 'hdfs',
  1409. definition: {
  1410. name: '/',
  1411. type: 'dir'
  1412. },
  1413. parent: null,
  1414. apiHelper: self.apiHelper
  1415. });
  1416. currentEntry.loadDeep(parts, function (entry) {
  1417. self.selectedHdfsEntry(entry);
  1418. entry.open(true);
  1419. self.loading(false);
  1420. });
  1421. };
  1422. self.reload = function () {
  1423. loadPath(self.apiHelper.getFromTotalStorage('assist', 'currentHdfsPath', '${ home_dir }'));
  1424. };
  1425. huePubSub.subscribe('assist.hdfs.go.home', function () {
  1426. loadPath('${ home_dir }');
  1427. self.apiHelper.setInTotalStorage('assist', 'currentHdfsPath', '${ home_dir }');
  1428. });
  1429. huePubSub.subscribe('assist.selectHdfsEntry', function (entry) {
  1430. self.selectedHdfsEntry(entry);
  1431. self.apiHelper.setInTotalStorage('assist', 'currentHdfsPath', entry.path);
  1432. });
  1433. huePubSub.subscribe('assist.hdfs.refresh', function () {
  1434. huePubSub.publish('assist.clear.hdfs.cache');
  1435. self.reload();
  1436. });
  1437. }
  1438. AssistHdfsPanel.prototype.init = function () {
  1439. var self = this;
  1440. if (self.initialized) {
  1441. return;
  1442. }
  1443. self.reload();
  1444. self.initialized = true;
  1445. };
  1446. function AssistAdlsPanel (options) {
  1447. var self = this;
  1448. self.apiHelper = options.apiHelper;
  1449. self.selectedAdlsEntry = ko.observable();
  1450. self.loading = ko.observable();
  1451. self.initialized = false;
  1452. var loadPath = function (path) {
  1453. self.loading(true);
  1454. var parts = path.split('/');
  1455. parts.shift();
  1456. var currentEntry = new AssistStorageEntry({
  1457. type: 'adls',
  1458. definition: {
  1459. name: '/',
  1460. type: 'dir'
  1461. },
  1462. parent: null,
  1463. apiHelper: self.apiHelper
  1464. });
  1465. currentEntry.loadDeep(parts, function (entry) {
  1466. self.selectedAdlsEntry(entry);
  1467. entry.open(true);
  1468. self.loading(false);
  1469. });
  1470. };
  1471. self.reload = function () {
  1472. loadPath(self.apiHelper.getFromTotalStorage('assist', 'currentAdlsPath', '/'));
  1473. };
  1474. huePubSub.subscribe('assist.adls.go.home', function () {
  1475. loadPath('${ home_dir }');
  1476. self.apiHelper.setInTotalStorage('assist', 'currentAdlsPath', '${ home_dir }');
  1477. });
  1478. huePubSub.subscribe('assist.selectAdlsEntry', function (entry) {
  1479. self.selectedAdlsEntry(entry);
  1480. self.apiHelper.setInTotalStorage('assist', 'currentAdlsPath', entry.path);
  1481. });
  1482. huePubSub.subscribe('assist.adls.refresh', function () {
  1483. huePubSub.publish('assist.clear.adls.cache');
  1484. self.reload();
  1485. });
  1486. }
  1487. AssistAdlsPanel.prototype.init = function () {
  1488. var self = this;
  1489. if (self.initialized) {
  1490. return;
  1491. }
  1492. self.reload();
  1493. self.initialized = true;
  1494. };
  1495. /**
  1496. * @param {Object} options
  1497. * @param {ApiHelper} options.apiHelper
  1498. * @constructor
  1499. **/
  1500. function AssistGitPanel (options) {
  1501. var self = this;
  1502. self.apiHelper = ApiHelper.getInstance();
  1503. self.selectedGitEntry = ko.observable();
  1504. self.reload = function () {
  1505. var lastKnownPath = self.apiHelper.getFromTotalStorage('assist', 'currentGitPath', '${ home_dir }');
  1506. var parts = lastKnownPath.split('/');
  1507. parts.shift();
  1508. var currentEntry = new AssistGitEntry({
  1509. definition: {
  1510. name: '/',
  1511. type: 'dir'
  1512. },
  1513. parent: null,
  1514. apiHelper: self.apiHelper
  1515. });
  1516. currentEntry.loadDeep(parts, function (entry) {
  1517. self.selectedGitEntry(entry);
  1518. entry.open(true);
  1519. });
  1520. };
  1521. huePubSub.subscribe('assist.selectGitEntry', function (entry) {
  1522. self.selectedGitEntry(entry);
  1523. self.apiHelper.setInTotalStorage('assist', 'currentGitPath', entry.path);
  1524. });
  1525. huePubSub.subscribe('assist.git.refresh', function () {
  1526. huePubSub.publish('assist.clear.git.cache');
  1527. self.reload();
  1528. });
  1529. }
  1530. AssistGitPanel.prototype.init = function () {
  1531. this.reload();
  1532. };
  1533. /**
  1534. * @param {Object} options
  1535. * @param {ApiHelper} options.apiHelper
  1536. * @constructor
  1537. **/
  1538. function AssistS3Panel (options) {
  1539. var self = this;
  1540. self.apiHelper = options.apiHelper;
  1541. self.selectedS3Entry = ko.observable();
  1542. self.loading = ko.observable();
  1543. self.initialized = false;
  1544. self.reload = function () {
  1545. self.loading(true);
  1546. var lastKnownPath = self.apiHelper.getFromTotalStorage('assist', 'currentS3Path', '/');
  1547. var parts = lastKnownPath.split('/');
  1548. parts.shift();
  1549. var currentEntry = new AssistStorageEntry({
  1550. type: 's3',
  1551. definition: {
  1552. name: '/',
  1553. type: 'dir'
  1554. },
  1555. parent: null,
  1556. apiHelper: self.apiHelper
  1557. });
  1558. currentEntry.loadDeep(parts, function (entry) {
  1559. self.selectedS3Entry(entry);
  1560. entry.open(true);
  1561. self.loading(false);
  1562. });
  1563. };
  1564. huePubSub.subscribe('assist.selectS3Entry', function (entry) {
  1565. self.selectedS3Entry(entry);
  1566. self.apiHelper.setInTotalStorage('assist', 'currentS3Path', entry.path);
  1567. });
  1568. huePubSub.subscribe('assist.s3.refresh', function () {
  1569. huePubSub.publish('assist.clear.s3.cache');
  1570. self.reload();
  1571. });
  1572. }
  1573. AssistS3Panel.prototype.init = function () {
  1574. var self = this;
  1575. if (self.initialized) {
  1576. return;
  1577. }
  1578. self.reload();
  1579. self.initialized = true;
  1580. };
  1581. /**
  1582. * @param {Object} options
  1583. * @param {ApiHelper} options.apiHelper
  1584. * @constructor
  1585. **/
  1586. function AssistHBasePanel(options) {
  1587. var self = this;
  1588. self.apiHelper = options.apiHelper;
  1589. self.initialized = false;
  1590. var root = new AssistHBaseEntry({
  1591. definition: {
  1592. host: '',
  1593. name: '',
  1594. port: 0
  1595. },
  1596. apiHelper: self.apiHelper
  1597. });
  1598. self.selectedHBaseEntry = ko.observable();
  1599. self.reload = function () {
  1600. self.selectedHBaseEntry(root);
  1601. root.loadEntries(function () {
  1602. var lastOpenendPath = self.apiHelper.getFromTotalStorage('assist', 'last.opened.hbase.entry', null);
  1603. if (lastOpenendPath) {
  1604. root.entries().every(function (entry) {
  1605. if (entry.path === lastOpenendPath) {
  1606. entry.open();
  1607. return false;
  1608. }
  1609. return true;
  1610. })
  1611. }
  1612. });
  1613. };
  1614. self.selectedHBaseEntry.subscribe(function (newEntry) {
  1615. if (newEntry !== root || (newEntry === root && newEntry.loaded)) {
  1616. self.apiHelper.setInTotalStorage('assist', 'last.opened.hbase.entry', newEntry.path);
  1617. }
  1618. });
  1619. huePubSub.subscribe('assist.clickHBaseItem', function (entry) {
  1620. if (entry.definition.host) {
  1621. entry.loadEntries();
  1622. self.selectedHBaseEntry(entry);
  1623. }
  1624. else {
  1625. huePubSub.publish('assist.dblClickHBaseItem', entry);
  1626. }
  1627. });
  1628. huePubSub.subscribe('assist.clickHBaseRootItem', function (entry) {
  1629. self.reload();
  1630. });
  1631. var delayChangeHash = function (hash) {
  1632. window.setTimeout(function () {
  1633. window.location.hash = hash;
  1634. }, 0);
  1635. }
  1636. self.lastClickeHBaseEntry = null;
  1637. self.HBaseLoaded = false;
  1638. huePubSub.subscribeOnce('hbase.app.loaded', function() {
  1639. if (self.selectedHBaseEntry() && self.lastClickeHBaseEntry) {
  1640. delayChangeHash(self.selectedHBaseEntry().definition.name + '/' + self.lastClickeHBaseEntry.definition.name);
  1641. }
  1642. self.HBaseLoaded = true;
  1643. });
  1644. huePubSub.subscribe('assist.dblClickHBaseItem', function (entry) {
  1645. var hash = self.selectedHBaseEntry().definition.name + '/' + entry.definition.name;
  1646. if (IS_HUE_4) {
  1647. if (window.location.pathname.startsWith('/hue/hbase')) {
  1648. window.location.hash = hash;
  1649. }
  1650. else {
  1651. self.lastClickeHBaseEntry = entry;
  1652. huePubSub.subscribeOnce('app.gained.focus', function (app) {
  1653. if (app === 'hbase' && self.HBaseLoaded) {
  1654. delayChangeHash(hash);
  1655. }
  1656. });
  1657. huePubSub.publish('open.link', '/hbase');
  1658. }
  1659. }
  1660. else {
  1661. window.open('/hbase/#' + hash);
  1662. }
  1663. });
  1664. huePubSub.subscribe('assist.hbase.refresh', function () {
  1665. huePubSub.publish('assist.clear.hbase.cache');
  1666. self.reload();
  1667. });
  1668. }
  1669. AssistHBasePanel.prototype.init = function () {
  1670. var self = this;
  1671. if (self.initialized) {
  1672. return;
  1673. }
  1674. self.reload();
  1675. self.initialized = true;
  1676. };
  1677. var NAV_FACET_ICON = 'fa-tags';
  1678. var NAV_TYPE_ICONS = {
  1679. 'DATABASE': 'fa-database',
  1680. 'TABLE': 'fa-table',
  1681. 'VIEW': 'fa-eye',
  1682. 'FIELD': 'fa-columns',
  1683. 'PARTITION': 'fa-th',
  1684. 'SOURCE': 'fa-server',
  1685. 'OPERATION': 'fa-cogs',
  1686. 'OPERATION_EXECUTION': 'fa-cog',
  1687. 'DIRECTORY': 'fa-folder-o',
  1688. 'FILE': 'fa-file-o',
  1689. 'S3BUCKET': 'fa-cubes',
  1690. 'SUB_OPERATION': 'fa-code-fork',
  1691. 'COLLECTION': 'fa-search',
  1692. 'HBASE': 'fa-th-large',
  1693. 'HUE': 'fa-file-o'
  1694. };
  1695. /**
  1696. * @param {Object} params
  1697. * @param {string} params.user
  1698. * @param {boolean} params.onlySql - For the old query editors
  1699. * @param {string[]} params.visibleAssistPanels - Panels that will initially be shown regardless of total storage
  1700. * @param {Object} params.sql
  1701. * @param {Object[]} params.sql.sourceTypes - All the available SQL source types
  1702. * @param {string} params.sql.sourceTypes[].name - Example: Hive SQL
  1703. * @param {string} params.sql.sourceTypes[].type - Example: hive
  1704. * @param {string} [params.sql.activeSourceType] - Example: hive
  1705. * @param {Object} params.sql.navigationSettings - enable/disable the links
  1706. * @param {boolean} params.sql.navigationSettings.openItem - Example: true
  1707. * @param {boolean} params.sql.navigationSettings.showStats - Example: true
  1708. * @constructor
  1709. */
  1710. function AssistPanel (params) {
  1711. var self = this;
  1712. var i18n = {
  1713. errorLoadingDatabases: "${ _('There was a problem loading the databases') }",
  1714. errorLoadingTablePreview: "${ _('There was a problem loading the table preview') }"
  1715. };
  1716. var i18nCollections = {
  1717. errorLoadingDatabases: "${ _('There was a problem loading the collections') }",
  1718. errorLoadingTablePreview: "${ _('There was a problem loading the collection preview') }"
  1719. };
  1720. self.apiHelper = ApiHelper.getInstance();
  1721. self.tabsEnabled = '${ USE_NEW_SIDE_PANELS.get() }' === 'True';
  1722. self.availablePanels = ko.observableArray();
  1723. self.visiblePanel = ko.observable();
  1724. self.lastOpenPanelType = ko.observable();
  1725. self.apiHelper.withTotalStorage('assist', 'last.open.panel', self.lastOpenPanelType);
  1726. huePubSub.subscribeOnce('cluster.config.set.config', function (clusterConfig) {
  1727. if (clusterConfig && clusterConfig['app_config']) {
  1728. var panels = [];
  1729. var appConfig = clusterConfig['app_config'];
  1730. if (appConfig['editor']) {
  1731. var sqlPanel = new AssistInnerPanel({
  1732. panelData: new AssistDbPanel($.extend({
  1733. apiHelper: self.apiHelper,
  1734. i18n: i18n
  1735. }, params.sql)),
  1736. apiHelper: self.apiHelper,
  1737. name: '${ _("SQL") }',
  1738. type: 'sql',
  1739. icon: 'fa-database',
  1740. minHeight: 75
  1741. });
  1742. panels.push(sqlPanel);
  1743. huePubSub.subscribe('assist.show.sql', function () {
  1744. if (self.visiblePanel() !== sqlPanel) {
  1745. self.visiblePanel(sqlPanel);
  1746. }
  1747. });
  1748. }
  1749. if (self.tabsEnabled) {
  1750. if (appConfig['browser'] && appConfig['browser']['interpreter_names'].indexOf('hdfs') != -1) {
  1751. panels.push(new AssistInnerPanel({
  1752. panelData: new AssistHdfsPanel({
  1753. apiHelper: self.apiHelper
  1754. }),
  1755. apiHelper: self.apiHelper,
  1756. name: '${ _("HDFS") }',
  1757. type: 'hdfs',
  1758. icon: 'fa-files-o',
  1759. minHeight: 50
  1760. }));
  1761. }
  1762. if (appConfig['browser'] && appConfig['browser']['interpreter_names'].indexOf('s3') != -1) {
  1763. panels.push(new AssistInnerPanel({
  1764. panelData: new AssistS3Panel({
  1765. apiHelper: self.apiHelper
  1766. }),
  1767. apiHelper: self.apiHelper,
  1768. name: '${ _("S3") }',
  1769. type: 's3',
  1770. icon: 'fa-cubes',
  1771. minHeight: 50
  1772. }));
  1773. }
  1774. if (appConfig['browser'] && appConfig['browser']['interpreter_names'].indexOf('adls') != -1) {
  1775. panels.push(new AssistInnerPanel({
  1776. panelData: new AssistAdlsPanel({
  1777. apiHelper: self.apiHelper
  1778. }),
  1779. apiHelper: self.apiHelper,
  1780. name: '${ _("ADLS") }',
  1781. type: 'adls',
  1782. icon: 'fa-windows',
  1783. iconSvg: '#hi-adls',
  1784. minHeight: 50
  1785. }));
  1786. }
  1787. if (appConfig['browser'] && appConfig['browser']['interpreter_names'].indexOf('indexes') != -1) {
  1788. var solrPanel = new AssistInnerPanel({
  1789. panelData: new AssistDbPanel($.extend({
  1790. apiHelper: self.apiHelper,
  1791. i18n: i18nCollections,
  1792. isSolr: true
  1793. }, params.sql)),
  1794. apiHelper: self.apiHelper,
  1795. name: '${ _("Collections") }',
  1796. type: 'solr',
  1797. icon: 'fa-search-plus',
  1798. minHeight: 75
  1799. });
  1800. panels.push(solrPanel);
  1801. huePubSub.subscribe('assist.show.solr', function () {
  1802. if (self.visiblePanel() !== solrPanel) {
  1803. self.visiblePanel(solrPanel);
  1804. }
  1805. });
  1806. }
  1807. if (appConfig['browser'] && appConfig['browser']['interpreter_names'].indexOf('hbase') != -1) {
  1808. panels.push(new AssistInnerPanel({
  1809. panelData: new AssistHBasePanel({
  1810. apiHelper: self.apiHelper
  1811. }),
  1812. apiHelper: self.apiHelper,
  1813. name: '${ _("HBase") }',
  1814. type: 'hbase',
  1815. icon: 'fa-th-large',
  1816. minHeight: 50
  1817. }));
  1818. }
  1819. % if not IS_EMBEDDED.get():
  1820. var documentsPanel = new AssistInnerPanel({
  1821. panelData: new AssistDocumentsPanel({
  1822. user: params.user,
  1823. apiHelper: self.apiHelper
  1824. }),
  1825. apiHelper: self.apiHelper,
  1826. name: '${ _("Documents") }',
  1827. type: 'documents',
  1828. icon: 'fa-files-o',
  1829. iconSvg: '#hi-documents',
  1830. minHeight: 50,
  1831. rightAlignIcon: true,
  1832. visible: params.visibleAssistPanels && params.visibleAssistPanels.indexOf('documents') !== -1
  1833. });
  1834. panels.push(documentsPanel);
  1835. huePubSub.subscribe('assist.show.documents', function (docType) {
  1836. huePubSub.publish('left.assist.show');
  1837. if (self.visiblePanel() !== documentsPanel) {
  1838. self.visiblePanel(documentsPanel);
  1839. }
  1840. if (docType) {
  1841. documentsPanel.panelData.setTypeFilter(docType);
  1842. }
  1843. });
  1844. % endif
  1845. var vcsKeysLength = ${ len(VCS.keys()) };
  1846. if (vcsKeysLength > 0) {
  1847. panels.push(new AssistInnerPanel({
  1848. panelData: new AssistGitPanel({
  1849. apiHelper: self.apiHelper
  1850. }),
  1851. apiHelper: self.apiHelper,
  1852. name: '${ _("Git") }',
  1853. type: 'git',
  1854. icon: 'fa-github',
  1855. minHeight: 50,
  1856. rightAlignIcon: true
  1857. }));
  1858. }
  1859. }
  1860. self.availablePanels(panels);
  1861. } else {
  1862. self.availablePanels([new AssistInnerPanel({
  1863. panelData: new AssistDbPanel($.extend({
  1864. apiHelper: self.apiHelper,
  1865. i18n: i18n
  1866. }, params.sql)),
  1867. apiHelper: self.apiHelper,
  1868. name: '${ _("SQL") }',
  1869. type: 'sql',
  1870. icon: 'fa-database',
  1871. minHeight: 75
  1872. })]);
  1873. }
  1874. if (!self.lastOpenPanelType()) {
  1875. self.lastOpenPanelType(self.availablePanels()[0].type);
  1876. }
  1877. var lastFoundPanel = self.availablePanels().filter(function (panel) { return panel.type === self.lastOpenPanelType() });
  1878. // always forces the db panel to load if not the last open panel
  1879. var dbPanel = self.availablePanels().filter(function (panel) { return panel.type === 'sql' });
  1880. if (dbPanel.length > 0 && (lastFoundPanel.length === 0 || (lastFoundPanel[0] !== dbPanel[0]))) {
  1881. dbPanel[0].panelData.init();
  1882. }
  1883. self.visiblePanel.subscribe(function(newValue) {
  1884. self.lastOpenPanelType(newValue.type);
  1885. if (newValue.type !== 'sql' && !newValue.panelData.initialized) {
  1886. newValue.panelData.init();
  1887. }
  1888. });
  1889. self.visiblePanel(lastFoundPanel.length === 1 ? lastFoundPanel[0] : self.availablePanels()[0]);
  1890. });
  1891. window.setTimeout(function () {
  1892. // Main initialization trigger in hue.mako, this is for Hue 3
  1893. if (self.availablePanels().length === 0) {
  1894. huePubSub.publish('cluster.config.get.config');
  1895. }
  1896. }, 0);
  1897. }
  1898. ko.components.register('assist-panel', {
  1899. viewModel: AssistPanel,
  1900. template: { element: 'assist-panel-template' }
  1901. });
  1902. })();
  1903. </script>
  1904. <script type="text/html" id="functions-panel-template">
  1905. <div class="assist-inner-panel">
  1906. <div class="assist-flex-panel">
  1907. <div class="assist-flex-header">
  1908. <div class="assist-inner-header">
  1909. <div class="function-dialect-dropdown" data-bind="component: { name: 'hue-drop-down', params: { fixedPosition: true, value: activeType, entries: availableTypes, linkTitle: '${ _ko('Selected dialect') }' } }" style="display: inline-block"></div>
  1910. </div>
  1911. </div>
  1912. <div class="assist-flex-search">
  1913. <div class="assist-filter">
  1914. <input class="clearable" type="text" placeholder="Filter..." data-bind="clearable: query, value: query, valueUpdate: 'afterkeydown'">
  1915. </div>
  1916. </div>
  1917. <div data-bind="css: { 'assist-flex-fill': !selectedFunction(), 'assist-flex-half': selectedFunction() }">
  1918. <!-- ko ifnot: query -->
  1919. <ul class="assist-function-categories" data-bind="foreach: activeCategories">
  1920. <li>
  1921. <a class="black-link" href="javascript: void(0);" data-bind="toggle: open"><i class="fa fa-fw" data-bind="css: { 'fa-chevron-right': !open(), 'fa-chevron-down': open }"></i> <span data-bind="text: name"></span></a>
  1922. <ul class="assist-functions" data-bind="slideVisible: open, foreach: functions">
  1923. <li data-bind="tooltip: { title: description, placement: 'left', delay: 1000 }">
  1924. <a class="assist-field-link" href="javascript: void(0);" data-bind="draggableText: { text: draggable, meta: { type: 'function' } }, css: { 'blue': $parents[1].selectedFunction() === $data }, multiClick: { click: function () { $parents[1].selectedFunction($data); }, dblClick: function () { huePubSub.publish('editor.insert.at.cursor', draggable); } }, text: signature"></a>
  1925. </li>
  1926. </ul>
  1927. </li>
  1928. </ul>
  1929. <!-- /ko -->
  1930. <!-- ko if: query -->
  1931. <!-- ko if: filteredFunctions().length > 0 -->
  1932. <ul class="assist-functions" data-bind="foreach: filteredFunctions">
  1933. <li data-bind="tooltip: { title: description, placement: 'left', delay: 1000 }">
  1934. <a class="assist-field-link" href="javascript: void(0);" data-bind="draggableText: { text: draggable, meta: { type: 'function' } }, css: { 'blue': $parent.selectedFunction() === $data }, multiClick: { click: function () { $parent.selectedFunction($data); }, dblClick: function () { huePubSub.publish('editor.insert.at.cursor', draggable); } }, html: signatureMatch"></a>
  1935. </li>
  1936. </ul>
  1937. <!-- /ko -->
  1938. <!-- ko if: filteredFunctions().length === 0 -->
  1939. <ul class="assist-functions">
  1940. <li class="assist-no-entries">${ _('No functions found. ') }</li>
  1941. </ul>
  1942. <!-- /ko -->
  1943. <!-- /ko -->
  1944. </div>
  1945. <!-- ko if: selectedFunction -->
  1946. <div class="assist-flex-half assist-function-details" data-bind="with: selectedFunction">
  1947. <div class="assist-panel-close"><button class="close" data-bind="click: function() { $parent.selectedFunction(null); }">&times;</button></div>
  1948. <div class="assist-function-signature blue" data-bind="draggableText: { text: draggable, meta: { type: 'function' } }, text: signature, event: { 'dblclick': function () { huePubSub.publish('editor.insert.at.cursor', draggable); } }"></div>
  1949. <!-- ko if: description -->
  1950. <div data-bind="html: descriptionMatch"></div>
  1951. <!-- /ko -->
  1952. </div>
  1953. <!-- /ko -->
  1954. </div>
  1955. </div>
  1956. </script>
  1957. <script type="text/javascript">
  1958. (function () {
  1959. function FunctionsPanel(params) {
  1960. var self = this;
  1961. self.categories = {};
  1962. self.disposals = [];
  1963. self.activeType = ko.observable();
  1964. self.availableTypes = ko.observableArray(['Hive', 'Impala', 'Pig']);
  1965. self.query = ko.observable();
  1966. self.selectedFunction = ko.observable();
  1967. self.availableTypes().forEach(function (type) {
  1968. self.initFunctions(type);
  1969. });
  1970. var selectedFunctionPerType = { 'Hive': null, 'Impala': null, 'Pig': null };
  1971. self.selectedFunction.subscribe(function (newFunction) {
  1972. if (newFunction) {
  1973. selectedFunctionPerType[self.activeType()] = newFunction;
  1974. if (!newFunction.category.open()) {
  1975. newFunction.category.open(true);
  1976. }
  1977. }
  1978. });
  1979. self.activeCategories = ko.observableArray();
  1980. self.filteredFunctions = ko.pureComputed(function () {
  1981. var result = [];
  1982. var lowerCaseQuery = self.query().toLowerCase();
  1983. var replaceRegexp = new RegExp('(' + lowerCaseQuery + ')', 'i');
  1984. self.activeCategories().forEach(function (category) {
  1985. category.functions.forEach(function (fn) {
  1986. if (fn.signature.toLowerCase().indexOf(lowerCaseQuery) === 0) {
  1987. fn.weight = 2;
  1988. fn.signatureMatch(fn.signature.replace(replaceRegexp, '<b>$1</b>'));
  1989. fn.descriptionMatch(fn.description);
  1990. result.push(fn);
  1991. } else if (fn.signature.toLowerCase().indexOf(lowerCaseQuery) !== -1) {
  1992. fn.weight = 1;
  1993. fn.signatureMatch(fn.signature.replace(replaceRegexp, '<b>$1</b>'));
  1994. fn.descriptionMatch(fn.description);
  1995. result.push(fn);
  1996. } else if ((fn.description && fn.description.toLowerCase().indexOf(lowerCaseQuery) !== -1)) {
  1997. fn.signatureMatch(fn.signature);
  1998. fn.descriptionMatch(fn.description.replace(replaceRegexp, '<b>$1</b>'));
  1999. fn.weight = 0;
  2000. result.push(fn);
  2001. } else {
  2002. if (fn.signatureMatch() !== fn.signature) {
  2003. fn.signatureMatch(fn.signature);
  2004. }
  2005. if (fn.descriptionMatch() !== fn.desciption) {
  2006. fn.descriptionMatch(fn.description);
  2007. }
  2008. }
  2009. });
  2010. });
  2011. result.sort(function (a, b) {
  2012. if (a.weight !== b.weight) {
  2013. return b.weight - a.weight;
  2014. }
  2015. return a.signature.localeCompare(b.signature);
  2016. });
  2017. return result;
  2018. });
  2019. var apiHelper = ApiHelper.getInstance();
  2020. self.activeType.subscribe(function (newType) {
  2021. self.selectedFunction(selectedFunctionPerType[newType]);
  2022. self.activeCategories(self.categories[newType]);
  2023. apiHelper.setInTotalStorage('assist', 'function.panel.active.type', newType);
  2024. });
  2025. var lastActiveType = apiHelper.getFromTotalStorage('assist', 'function.panel.active.type', self.availableTypes()[0]);
  2026. self.activeType(lastActiveType);
  2027. var updateType = function (type) {
  2028. self.availableTypes().every(function (availableType) {
  2029. if (availableType.toLowerCase() === type) {
  2030. if (self.activeType() !== availableType) {
  2031. self.activeType(availableType);
  2032. }
  2033. return false;
  2034. }
  2035. return true;
  2036. });
  2037. };
  2038. var activeSnippetTypeSub = huePubSub.subscribe('active.snippet.type.changed', updateType);
  2039. self.disposals.push(function () {
  2040. activeSnippetTypeSub.remove();
  2041. });
  2042. huePubSub.subscribeOnce('set.active.snippet.type', updateType);
  2043. huePubSub.publish('get.active.snippet.type');
  2044. }
  2045. FunctionsPanel.prototype.dispose = function () {
  2046. var self = this;
  2047. self.disposals.forEach(function (dispose) {
  2048. dispose();
  2049. })
  2050. };
  2051. FunctionsPanel.prototype.initFunctions = function (dialect) {
  2052. var self = this;
  2053. self.categories[dialect] = [];
  2054. var functions = dialect === 'Pig' ? PigFunctions.CATEGORIZED_FUNCTIONS : SqlFunctions.CATEGORIZED_FUNCTIONS[dialect.toLowerCase()];
  2055. functions.forEach(function (category) {
  2056. var koCategory = {
  2057. name: category.name,
  2058. open: ko.observable(false),
  2059. functions: $.map(category.functions, function(fn) {
  2060. return {
  2061. draggable: fn.draggable,
  2062. signature: fn.signature,
  2063. signatureMatch: ko.observable(fn.signature),
  2064. description: fn.description,
  2065. descriptionMatch: ko.observable(fn.description)
  2066. }
  2067. })
  2068. };
  2069. koCategory.functions.forEach(function (fn) {
  2070. fn.category = koCategory;
  2071. });
  2072. self.categories[dialect].push(koCategory)
  2073. });
  2074. };
  2075. ko.components.register('functions-panel', {
  2076. viewModel: FunctionsPanel,
  2077. template: { element: 'functions-panel-template' }
  2078. });
  2079. })();
  2080. </script>
  2081. <script type="text/html" id="editor-assistant-panel-template">
  2082. <div class="assist-inner-panel assist-assistant-panel">
  2083. <div class="assist-flex-panel">
  2084. <div class="assist-flex-header">
  2085. <div class="assist-inner-header">
  2086. <!-- ko if: isSolr -->
  2087. ${ _('Collections') }
  2088. <!-- /ko -->
  2089. <!-- ko ifnot: isSolr -->
  2090. ${ _('Tables') }
  2091. <!-- ko if: statementCount() > 1 -->
  2092. <div class="statement-count">${ _('Statement') } <span data-bind="text: activeStatementIndex() + '/' + statementCount()"></span></div>
  2093. <!-- /ko -->
  2094. <!-- /ko -->
  2095. </div>
  2096. </div>
  2097. <div class="assist-flex-search" data-bind="visible: activeTables().length > 0">
  2098. <div class="assist-filter">
  2099. <!-- ko component: {
  2100. name: 'inline-autocomplete',
  2101. params: {
  2102. querySpec: filter.querySpec,
  2103. facets: ['type'],
  2104. knownFacetValues: isSolr() ? SOLR_ASSIST_KNOWN_FACET_VALUES : SQL_ASSIST_KNOWN_FACET_VALUES,
  2105. autocompleteFromEntries: $component.autocompleteFromEntries
  2106. }
  2107. } --><!-- /ko -->
  2108. </div>
  2109. </div>
  2110. <div class="assist-flex-half assist-db-scrollable">
  2111. <!-- ko if: filteredTables().length === 0 && (!filter.querySpec() || filter.querySpec().query === '') -->
  2112. <div class="assist-no-entries">
  2113. <!-- ko if: isSolr -->
  2114. ${ _('No collections selected.') }
  2115. <!-- /ko -->
  2116. <!-- ko ifnot: isSolr -->
  2117. ${ _('No tables identified.') }
  2118. <!-- /ko -->
  2119. </div>
  2120. <!-- /ko -->
  2121. <!-- ko if: filteredTables().length === 0 && filter.querySpec() && filter.querySpec().query !== '' && !someLoading() -->
  2122. <div class="assist-no-entries">${ _('No entries found.') }</div>
  2123. <!-- /ko -->
  2124. <!-- ko if: filteredTables().length > 0 -->
  2125. <ul class="database-tree assist-tables" data-bind="foreachVisible: { data: filteredTables, minHeight: 22, container: '.assist-db-scrollable', skipScrollEvent: true }">
  2126. <!-- ko if: hasErrors -->
  2127. <li class="assist-table hue-warning" data-bind="attr: { 'title': $parent.isSolr() ? '${ _ko('Error loading collection details.') }' : '${ _ko('Error loading table details.') }'}">
  2128. <span class="assist-entry">
  2129. <i class="hue-warning fa fa-fw muted valign-middle fa-warning"></i>
  2130. <span data-bind="text: catalogEntry.getDisplayName()"></span>
  2131. </span>
  2132. </li>
  2133. <!-- /ko -->
  2134. <!-- ko ifnot: hasErrors -->
  2135. <!-- ko template: { if: catalogEntry.isTableOrView(), name: 'assist-table-entry' } --><!-- /ko -->
  2136. <!-- ko template: { if: catalogEntry.isField(), name: 'assist-column-entry-assistant' } --><!-- /ko -->
  2137. <!-- /ko -->
  2138. </ul>
  2139. <!-- /ko -->
  2140. <!-- ko hueSpinner: { spin: filter.querySpec() && filter.querySpec().query !== '' && someLoading(), inline: true, center: true} --><!-- /ko -->
  2141. </div>
  2142. <!-- ko if: HAS_OPTIMIZER && !isSolr() -->
  2143. <div class="assist-flex-header assist-divider"><div class="assist-inner-header">${ _('Suggestions') }</div></div>
  2144. <div class="assist-flex-half">
  2145. <!-- ko if: ! activeRisks().hints -->
  2146. <div class="assist-no-entries">${ _('Select a query or start typing to get optimization hints.') }</div>
  2147. <!-- /ko -->
  2148. <!-- ko if: activeRisks().hints && activeRisks().hints.length === 0 -->
  2149. <div class="assist-no-entries">${ _('No optimizations identified.') }</div>
  2150. <!-- /ko -->
  2151. <!-- ko if: activeRisks().hints && activeRisks().hints.length > 0 -->
  2152. <ul class="risk-list" data-bind="foreach: activeRisks().hints">
  2153. <li>
  2154. <div class="risk-list-title" data-bind="css: { 'risk-list-high' : risk === 'high', 'risk-list-normal': risk !== 'high' }, tooltip: { title: risk + ' ' + riskTables }"><span data-bind="text: riskAnalysis"></span></div>
  2155. <div class="risk-list-description" data-bind="text: riskRecommendation"></div>
  2156. <div class="risk-quickfix" data-bind="visible: (riskId === 17 || riskId === 22) && $parent.activeEditor() && $parent.activeLocations()" style="display:none;">
  2157. <a href="javascript:void(0);" data-bind="click: function () { $parent.addFilter(riskId); hueAnalytics.convert('optimizer', 'addFilter/' + riskId); }">${ _('Add filter') }</a>
  2158. </div>
  2159. </li>
  2160. </ul>
  2161. <!-- /ko -->
  2162. <!-- ko if: hasMissingRisks() -->
  2163. <div class="margin-top-20">
  2164. <!-- ko hueSpinner: { spin: uploadingTableStats, inline: true} --><!-- /ko -->
  2165. <!-- ko ifnot: uploadingTableStats -->
  2166. <a href="javascript:void(0)" data-bind="visible: activeTables().length > 0, click: function() { uploadTableStats(true) }, attr: { 'title': ('${ _("Add table ") }' + (isMissingDDL() ? 'DDL' : '') + (isMissingDDL() && isMissingStats() ? ' ${ _("and") } ' : '') + (isMissingStats() ? 'stats' : '')) }">
  2167. <i class="fa fa-fw fa-plus-circle"></i> ${_('Improve Analysis')}
  2168. </a>
  2169. <!-- /ko -->
  2170. </div>
  2171. <!-- /ko -->
  2172. </div>
  2173. <!-- /ko -->
  2174. </div>
  2175. </div>
  2176. </script>
  2177. <script type="text/javascript">
  2178. var AssistantUtils = (function () {
  2179. return {
  2180. getFilteredTablesPureComputed: function (vm) {
  2181. var openedByFilter = [];
  2182. return ko.pureComputed(function () {
  2183. if (vm.filter == null || !vm.filter.querySpec() || ((!vm.filter.querySpec().facets || Object.keys(vm.filter.querySpec().facets).length === 0) && (!vm.filter.querySpec().text || vm.filter.querySpec().text.length === 0))) {
  2184. while (openedByFilter.length) {
  2185. openedByFilter.pop().open(false);
  2186. }
  2187. return vm.activeTables();
  2188. }
  2189. var facets = vm.filter.querySpec().facets;
  2190. var result = [];
  2191. $.each(vm.activeTables(), function (index, entry) {
  2192. var facetMatch = !facets || !facets['type'] || (!facets['type']['table'] && !facets['type']['view']);
  2193. if (!facetMatch && facets['type']['table']) {
  2194. facetMatch = entry.catalogEntry.isTable();
  2195. }
  2196. if (!facetMatch && facets['type']['view']) {
  2197. facetMatch = entry.catalogEntry.isView();
  2198. }
  2199. var textMatch = !vm.filter.querySpec().text || vm.filter.querySpec().text.length === 0;
  2200. if (!textMatch) {
  2201. var nameLower = entry.catalogEntry.name.toLowerCase();
  2202. textMatch = vm.filter.querySpec().text.every(function (text) {
  2203. return nameLower.indexOf(text.toLowerCase()) !== -1;
  2204. });
  2205. }
  2206. entry.filterColumnNames(!textMatch);
  2207. if ((facetMatch && textMatch) || entry.filteredEntries().length > 0) {
  2208. if (!entry.open()) {
  2209. entry.open(true);
  2210. openedByFilter.push(entry);
  2211. }
  2212. result.push(entry);
  2213. }
  2214. });
  2215. return result;
  2216. });
  2217. }
  2218. }
  2219. })();
  2220. (function () {
  2221. function EditorAssistantPanel(params) {
  2222. var self = this;
  2223. self.disposals = [];
  2224. self.isSolr = ko.observable(false);
  2225. self.activeTab = params.activeTab;
  2226. self.uploadingTableStats = ko.observable(false);
  2227. self.activeStatement = ko.observable();
  2228. self.activeTables = ko.observableArray();
  2229. self.activeRisks = ko.observable({});
  2230. self.activeEditor = ko.observable();
  2231. self.activeRisks.subscribe(function() {
  2232. if (self.isMissingDDL()) {
  2233. self.uploadTableStats(false);
  2234. }
  2235. });
  2236. self.activeLocations = ko.observable();
  2237. self.statementCount = ko.observable(0);
  2238. self.activeStatementIndex = ko.observable(0);
  2239. self.hasActiveRisks = ko.pureComputed(function () {
  2240. return self.activeRisks().hints && self.activeRisks().hints.length > 0;
  2241. });
  2242. self.hasMissingRisks = ko.pureComputed(function () {
  2243. return self.isMissingDDL() || self.isMissingStats();
  2244. });
  2245. self.isMissingDDL = ko.pureComputed(function () {
  2246. return self.activeRisks().noDDL && self.activeRisks().noDDL.length > 0
  2247. });
  2248. self.isMissingStats = ko.pureComputed(function () {
  2249. % if OPTIMIZER.AUTO_UPLOAD_STATS.get():
  2250. return self.activeRisks().noStats && self.activeRisks().noStats.length > 0;
  2251. % else:
  2252. return false;
  2253. % endif
  2254. });
  2255. self.someLoading = ko.pureComputed(function () {
  2256. return self.activeTables().some(function (table) {
  2257. return table.loading() || (!table.hasEntries() && !table.hasErrors());
  2258. });
  2259. });
  2260. var createQualifiedIdentifier = function (identifierChain, defaultDatabase) {
  2261. if (identifierChain.length === 1) {
  2262. return defaultDatabase + '.' + identifierChain[0].name;
  2263. }
  2264. return $.map(identifierChain, function (identifier) {
  2265. return identifier.name;
  2266. }).join('.').toLowerCase();
  2267. };
  2268. var databaseIndex = {};
  2269. var activeTableIndex = {};
  2270. self.filter = {
  2271. querySpec: ko.observable({
  2272. query: '',
  2273. facets: {},
  2274. text: []
  2275. }),
  2276. showViews: ko.observable(true),
  2277. showTables: ko.observable(true)
  2278. };
  2279. self.filteredTables = AssistantUtils.getFilteredTablesPureComputed(self);
  2280. var navigationSettings = {
  2281. showStats: true,
  2282. rightAssist: true
  2283. };
  2284. var i18n = {};
  2285. var assistDbSource = new AssistDbSource({
  2286. i18n : i18n,
  2287. type: 'hive',
  2288. name: 'hive',
  2289. navigationSettings: navigationSettings
  2290. });
  2291. var loadEntriesTimeout = -1;
  2292. // This fetches the columns for each table synchronously with 2 second in between.
  2293. var loadEntries = function (currentCount) {
  2294. var count = currentCount || 0;
  2295. count++;
  2296. if (count > 10) {
  2297. return;
  2298. }
  2299. window.clearTimeout(loadEntriesTimeout);
  2300. if (self.activeTables().length === 1) {
  2301. self.activeTables()[0].open(true);
  2302. } else {
  2303. loadEntriesTimeout = window.setTimeout(function () {
  2304. self.activeTables().every(function (table) {
  2305. if (!table.loaded && !table.hasErrors() && !table.loading()) {
  2306. table.loadEntries(function () {
  2307. loadEntries(count);
  2308. });
  2309. return false;
  2310. }
  2311. return !table.loading();
  2312. })
  2313. }, 2000);
  2314. }
  2315. };
  2316. self.autocompleteFromEntries = function (nonPartial, partial) {
  2317. var added = {};
  2318. var result = [];
  2319. var partialLower = partial.toLowerCase();
  2320. self.activeTables().forEach(function (table) {
  2321. if (!added[table.catalogEntry.name] && table.catalogEntry.name.toLowerCase().indexOf(partialLower) === 0) {
  2322. added[table.catalogEntry.name] = true;
  2323. result.push(nonPartial + partial + table.catalogEntry.name.substring(partial.length))
  2324. }
  2325. table.entries().forEach(function (col) {
  2326. if (!added[col.catalogEntry.name] && col.catalogEntry.name.toLowerCase().indexOf(partialLower) === 0) {
  2327. added[col.catalogEntry.name] = true;
  2328. result.push(nonPartial + partial + col.catalogEntry.name.substring(partial.length))
  2329. }
  2330. })
  2331. });
  2332. return result;
  2333. };
  2334. var activeTablesSub = self.activeTables.subscribe(loadEntries);
  2335. self.disposals.push(function () {
  2336. window.clearTimeout(loadEntriesTimeout);
  2337. activeTablesSub.dispose();
  2338. });
  2339. var updateOnVisible = false;
  2340. var runningPromises = [];
  2341. var handleLocationUpdate = function (activeLocations) {
  2342. while (runningPromises.length) {
  2343. var promise = runningPromises.pop();
  2344. if (promise.cancel) {
  2345. promise.cancel();
  2346. }
  2347. }
  2348. updateOnVisible = false;
  2349. assistDbSource.sourceType = activeLocations.type;
  2350. if (!activeLocations) {
  2351. self.activeLocations(undefined);
  2352. return;
  2353. }
  2354. self.activeLocations(activeLocations);
  2355. self.statementCount(activeLocations.totalStatementCount);
  2356. self.activeStatementIndex(activeLocations.activeStatementIndex);
  2357. if (activeLocations.activeStatementLocations) {
  2358. var updateTables = false;
  2359. var tableQidIndex = {};
  2360. var ctes = {};
  2361. activeLocations.activeStatementLocations.forEach(function (location) {
  2362. if (location.type === 'alias' && location.source === 'cte') {
  2363. ctes[location.alias.toLowerCase()] = true;
  2364. }
  2365. });
  2366. activeLocations.activeStatementLocations.forEach(function (location) {
  2367. if (location.type === 'table' && (location.identifierChain.length !== 1 || !ctes[location.identifierChain[0].name.toLowerCase()])) {
  2368. var tableDeferred = $.Deferred();
  2369. var dbDeferred = $.Deferred();
  2370. runningPromises.push(tableDeferred);
  2371. runningPromises.push(dbDeferred);
  2372. var qid = createQualifiedIdentifier(location.identifierChain, activeLocations.defaultDatabase);
  2373. if (activeTableIndex[qid]) {
  2374. tableQidIndex[qid] = true;
  2375. tableDeferred.resolve(activeTableIndex[qid]);
  2376. dbDeferred.resolve(activeTableIndex[qid].parent);
  2377. } else {
  2378. var database = location.identifierChain.length === 2 ? location.identifierChain[0].name : activeLocations.defaultDatabase;
  2379. database = database.toLowerCase();
  2380. if (databaseIndex[database]) {
  2381. dbDeferred.resolve(databaseIndex[database]);
  2382. } else {
  2383. DataCatalog.getEntry({
  2384. sourceType: activeLocations.type,
  2385. path: [ database ],
  2386. definition: { type: 'database' }
  2387. }).done(function (catalogEntry) {
  2388. databaseIndex[database] = new AssistDbEntry(catalogEntry, null, assistDbSource, self.filter, i18n,navigationSettings);
  2389. updateTables = true;
  2390. dbDeferred.resolve(databaseIndex[database])
  2391. }).fail(function () {
  2392. console.log('reject 1');
  2393. dbDeferred.reject();
  2394. });
  2395. }
  2396. dbDeferred.done(function (dbEntry) {
  2397. dbEntry.catalogEntry.getChildren().done(function (tableEntries) {
  2398. var tableName = location.identifierChain[location.identifierChain.length - 1].name;
  2399. var found = tableEntries.some(function (tableEntry) {
  2400. if (tableEntry.name === tableName) {
  2401. var assistTableEntry = new AssistDbEntry(
  2402. tableEntry,
  2403. dbEntry,
  2404. assistDbSource,
  2405. self.filter,
  2406. i18n,
  2407. navigationSettings
  2408. );
  2409. activeTableIndex[createQualifiedIdentifier(location.identifierChain, activeLocations.defaultDatabase)] = assistTableEntry;
  2410. tableQidIndex[qid] = true;
  2411. updateTables = true;
  2412. tableDeferred.resolve(assistTableEntry);
  2413. return true;
  2414. }
  2415. });
  2416. if (!found) {
  2417. var missingEntry = new AssistDbEntry(
  2418. {
  2419. path: [dbEntry.catalogEntry.name, tableName],
  2420. name: tableName,
  2421. isTableOrView: function () { return true; },
  2422. getType: function () { return 'table' },
  2423. hasPossibleChildren: function () { return true; },
  2424. getSourceMeta: function () { return $.Deferred().resolve({ notFound: true }).promise() },
  2425. getDisplayName: function () { return dbEntry.catalogEntry.name + '.' + tableName }
  2426. },
  2427. dbEntry,
  2428. assistDbSource,
  2429. self.filter,
  2430. i18n,
  2431. navigationSettings
  2432. );
  2433. activeTableIndex[createQualifiedIdentifier(location.identifierChain, activeLocations.defaultDatabase)] = missingEntry;
  2434. tableQidIndex[qid] = true;
  2435. updateTables = true;
  2436. missingEntry.hasErrors(true);
  2437. tableDeferred.resolve(missingEntry);
  2438. }
  2439. }).fail(tableDeferred.reject);
  2440. }).fail(tableDeferred.reject);
  2441. }
  2442. }
  2443. });
  2444. $.when.apply($, runningPromises).always(function () {
  2445. runningPromises.length = 0;
  2446. Object.keys(activeTableIndex).forEach(function (key) {
  2447. if (!tableQidIndex[key]) {
  2448. delete activeTableIndex[key];
  2449. updateTables = true;
  2450. }
  2451. });
  2452. if (updateTables) {
  2453. var tables = [];
  2454. Object.keys(activeTableIndex).forEach(function (key) {
  2455. tables.push(activeTableIndex[key]);
  2456. });
  2457. tables.sort(function (a, b) {
  2458. return a.catalogEntry.name.localeCompare(b.catalogEntry.name);
  2459. });
  2460. self.activeTables(tables);
  2461. }
  2462. });
  2463. }
  2464. };
  2465. if (self.activeTab() === 'editorAssistant') {
  2466. huePubSub.publish('get.active.editor.locations', handleLocationUpdate);
  2467. } else {
  2468. updateOnVisible = true;
  2469. }
  2470. var activeTabSub = self.activeTab.subscribe(function (activeTab) {
  2471. if (activeTab === 'editorAssistant' && updateOnVisible) {
  2472. huePubSub.publish('get.active.editor.locations', handleLocationUpdate);
  2473. }
  2474. });
  2475. self.disposals.push(function () {
  2476. activeTabSub.dispose();
  2477. });
  2478. var activeLocationsSub = huePubSub.subscribe('editor.active.locations', function (activeLocations) {
  2479. if (self.activeTab() === 'editorAssistant') {
  2480. handleLocationUpdate(activeLocations);
  2481. } else {
  2482. updateOnVisible = true;
  2483. }
  2484. });
  2485. var activeRisksSub = huePubSub.subscribe('editor.active.risks', function (details) {
  2486. if (details.risks !== self.activeRisks()) {
  2487. self.activeRisks(details.risks);
  2488. self.activeEditor(details.editor);
  2489. }
  2490. });
  2491. huePubSub.publish('editor.get.active.risks', function (details) {
  2492. self.activeRisks(details.risks);
  2493. self.activeEditor(details.editor);
  2494. });
  2495. self.disposals.push(function () {
  2496. activeLocationsSub.remove();
  2497. activeRisksSub.remove();
  2498. });
  2499. }
  2500. EditorAssistantPanel.prototype.addFilter = function (riskId) {
  2501. var self = this;
  2502. if (self.activeLocations() && self.activeEditor()) {
  2503. self.activeLocations().activeStatementLocations.every(function (location) {
  2504. var isLowerCase = false;
  2505. if (self.activeLocations().activeStatementLocations && self.activeLocations().activeStatementLocations.length > 0) {
  2506. var firstToken = self.activeLocations().activeStatementLocations[0].firstToken;
  2507. isLowerCase = firstToken === firstToken.toLowerCase();
  2508. }
  2509. if (location.type === 'whereClause' && !location.subquery && (location.missing || riskId === 22 )) {
  2510. self.activeEditor().moveCursorToPosition({
  2511. row: location.location.last_line - 1,
  2512. column: location.location.last_column - 1
  2513. });
  2514. self.activeEditor().clearSelection();
  2515. if (/\S$/.test(self.activeEditor().getTextBeforeCursor())) {
  2516. self.activeEditor().session.insert(self.activeEditor().getCursorPosition(), ' ');
  2517. }
  2518. var operation = location.missing ? 'WHERE ' : 'AND ';
  2519. self.activeEditor().session.insert(self.activeEditor().getCursorPosition(), isLowerCase ? operation.toLowerCase() : operation);
  2520. self.activeEditor().focus();
  2521. if (riskId === 22) {
  2522. huePubSub.publish('editor.autocomplete.temporary.sort.override', { partitionColumnsFirst: true });
  2523. }
  2524. window.setTimeout(function () {
  2525. self.activeEditor().execCommand("startAutocomplete");
  2526. }, 1);
  2527. return false;
  2528. }
  2529. return true;
  2530. })
  2531. }
  2532. };
  2533. EditorAssistantPanel.prototype.uploadTableStats = function (showProgress) {
  2534. var self = this;
  2535. if (self.uploadingTableStats()) {
  2536. return;
  2537. }
  2538. self.uploadingTableStats(true);
  2539. huePubSub.publish('editor.upload.table.stats', {
  2540. activeTables: self.activeTables(),
  2541. showProgress: showProgress,
  2542. callback: function () {
  2543. self.uploadingTableStats(false);
  2544. }
  2545. });
  2546. };
  2547. EditorAssistantPanel.prototype.dispose = function () {
  2548. var self = this;
  2549. self.disposals.forEach(function (dispose) {
  2550. dispose();
  2551. })
  2552. };
  2553. ko.components.register('editor-assistant-panel', {
  2554. viewModel: EditorAssistantPanel,
  2555. template: { element: 'editor-assistant-panel-template' }
  2556. });
  2557. })();
  2558. </script>
  2559. <script type="text/html" id="schedule-panel-template">
  2560. <div class="assist-inner-panel">
  2561. <div class="assist-flex-panel">
  2562. <!-- ko if: selectedNotebook() && selectedNotebook().isBatchable() -->
  2563. <!-- ko with: selectedNotebook() -->
  2564. <div class="tab-pane" id="scheduleTab">
  2565. <!-- ko ifnot: isSaved() && ! isHistory() -->
  2566. ${ _('Query needs to be saved.') }
  2567. <!-- /ko -->
  2568. <!-- ko if: isSaved() && ! isHistory() -->
  2569. <!-- ko if: schedulerViewModelIsLoaded() && schedulerViewModel.coordinator.isDirty() -->
  2570. <a data-bind="click: saveScheduler" href="javascript: void(0);">${ _('Save changes') }</a>
  2571. <!-- /ko -->
  2572. <!-- ko if: schedulerViewModelIsLoaded() && ! schedulerViewModel.coordinator.isDirty() && (! viewSchedulerId() || isSchedulerJobRunning() == false )-->
  2573. <a data-bind="click: showSubmitPopup" href="javascript: void(0);">${ _('Start') }</a>
  2574. <!-- /ko -->
  2575. <!-- ko if: schedulerViewModelIsLoaded() && viewSchedulerId()-->
  2576. <a data-bind="click: function() { huePubSub.publish('show.jobs.panel', {id: viewSchedulerId(), interface: 'schedules'}) }, clickBubble: false" href="javascript: void(0);">
  2577. ${ _('View') }
  2578. </a>
  2579. <!-- ko if: isSchedulerJobRunning() -->
  2580. ${ _("Running")}
  2581. <!-- /ko -->
  2582. <!-- ko if: isSchedulerJobRunning() == false -->
  2583. ${ _("Stopped")}
  2584. <!-- /ko -->
  2585. <!-- /ko -->
  2586. <!-- /ko -->
  2587. <br>
  2588. <br>
  2589. <div id="schedulerEditor"></div>
  2590. </div>
  2591. <!-- /ko -->
  2592. <!-- /ko -->
  2593. </div>
  2594. </div>
  2595. </script>
  2596. <script type="text/javascript">
  2597. (function () {
  2598. function SchedulePanel(params) {
  2599. var self = this;
  2600. self.disposals = [];
  2601. self.selectedNotebook = ko.observable();
  2602. // TODO: Move all the scheduler logic out of the notebook to here.
  2603. var selectedNotebookSub = self.selectedNotebook.subscribe(function (notebook) { // Happening 4 times for each notebook loaded
  2604. if (notebook && notebook.schedulerViewModel == null && notebook.isSaved() && ! notebook.isHistory()) {
  2605. notebook.loadScheduler();
  2606. if (notebook.viewSchedulerId()) {
  2607. huePubSub.publish('check.schedules.browser');
  2608. }
  2609. }
  2610. });
  2611. self.disposals.push(selectedNotebookSub.dispose.bind(selectedNotebookSub));
  2612. var setSelectedNotebookSub = huePubSub.subscribe('jobbrowser.schedule.data', function (jobs) {
  2613. if (self.selectedNotebook() && self.selectedNotebook().viewSchedulerId()) {
  2614. var _job = $.grep(jobs, function (job) {
  2615. return self.selectedNotebook().viewSchedulerId() == job.id;
  2616. });
  2617. self.selectedNotebook().isSchedulerJobRunning(_job.length > 0 && _job[0].apiStatus == 'RUNNING');
  2618. }
  2619. });
  2620. self.disposals.push(setSelectedNotebookSub.remove.bind(setSelectedNotebookSub));
  2621. // Hue 3
  2622. var setSelectedNotebookSub = huePubSub.subscribe('set.selected.notebook', self.selectedNotebook);
  2623. self.disposals.push(setSelectedNotebookSub.remove.bind(setSelectedNotebookSub));
  2624. var selectedNotebookChangedSub = huePubSub.subscribe('selected.notebook.changed', self.selectedNotebook);
  2625. self.disposals.push(selectedNotebookChangedSub.remove.bind(selectedNotebookChangedSub));
  2626. huePubSub.publish('get.selected.notebook');
  2627. // Hue 4
  2628. var currentAppSub = huePubSub.subscribe('set.current.app.view.model', function (viewModel) {
  2629. if (viewModel.selectedNotebook) {
  2630. if (viewModel.selectedNotebook()) {
  2631. self.selectedNotebook(viewModel.selectedNotebook());
  2632. } else {
  2633. var subscription = viewModel.selectedNotebook.subscribe(function (notebook) {
  2634. self.selectedNotebook(notebook);
  2635. subscription.dispose();
  2636. });
  2637. }
  2638. } else {
  2639. self.selectedNotebook(null);
  2640. }
  2641. });
  2642. self.disposals.push(currentAppSub.remove.bind(currentAppSub));
  2643. }
  2644. SchedulePanel.prototype.dispose = function () {
  2645. var self = this;
  2646. self.disposals.forEach(function (dispose) {
  2647. dispose();
  2648. })
  2649. };
  2650. ko.components.register('schedule-panel', {
  2651. viewModel: SchedulePanel,
  2652. template: { element: 'schedule-panel-template' }
  2653. });
  2654. })();
  2655. </script>
  2656. <script type="text/javascript">
  2657. (function () {
  2658. function DashboardAssistantPanel(params) {
  2659. var self = this;
  2660. self.disposals = [];
  2661. self.isSolr = ko.observable(true);
  2662. self.filter = {
  2663. querySpec: ko.observable({
  2664. query: '',
  2665. facets: {},
  2666. text: []
  2667. })
  2668. };
  2669. self.activeTables = ko.observableArray();
  2670. self.filteredTables = AssistantUtils.getFilteredTablesPureComputed(self);
  2671. self.someLoading = ko.pureComputed(function () {
  2672. return self.activeTables().some(function (table) {
  2673. return table.loading() || (!table.hasEntries() && !table.hasErrors());
  2674. });
  2675. });
  2676. var navigationSettings = {
  2677. showStats: true,
  2678. rightAssist: true
  2679. };
  2680. var i18n = {};
  2681. var activeDashboardCollection = huePubSub.subscribe('set.active.dashboard.collection', function(collection) {
  2682. var collectionName = collection.name();
  2683. var assistDbSource = new AssistDbSource({
  2684. i18n : i18n,
  2685. type: collection.engine(),
  2686. name: collection.engine(),
  2687. navigationSettings: navigationSettings
  2688. });
  2689. var fakeParentName = collectionName.indexOf('.') > -1 ? collectionName.split('.')[0] : 'default';
  2690. var sourceType = collection.source() === 'query' ? collection.engine() + '-query' : collection.engine();
  2691. DataCatalog.getEntry({
  2692. sourceType: sourceType,
  2693. path: [ fakeParentName ],
  2694. definition: { type: 'database' }
  2695. }).done(function (fakeDbCatalogEntry) {
  2696. var assistFakeDb = new AssistDbEntry(fakeDbCatalogEntry, null, assistDbSource, self.filter, i18n, navigationSettings);
  2697. DataCatalog.getEntry({
  2698. sourceType: sourceType,
  2699. path: [fakeParentName, collectionName.indexOf('.') > -1 ? collectionName.split('.')[1] : collectionName],
  2700. definition: { type: 'table' }
  2701. }).done(function (collectionCatalogEntry) {
  2702. var collectionEntry = new AssistDbEntry(collectionCatalogEntry, assistFakeDb, assistDbSource, self.filter, i18n, navigationSettings);
  2703. self.activeTables([collectionEntry]);
  2704. if (!collectionEntry.loaded && !collectionEntry.hasErrors() && !collectionEntry.loading()) {
  2705. collectionEntry.loadEntries(function() { collectionEntry.toggleOpen(); });
  2706. }
  2707. });
  2708. });
  2709. self.autocompleteFromEntries = function (nonPartial, partial) {
  2710. var added = {};
  2711. var result = [];
  2712. var partialLower = partial.toLowerCase();
  2713. self.activeTables().forEach(function (table) {
  2714. if (!added[table.catalogEntry.name] && table.catalogEntry.name.toLowerCase().indexOf(partialLower) === 0) {
  2715. added[table.catalogEntry.name] = true;
  2716. result.push(nonPartial + partial + table.catalogEntry.name.substring(partial.length))
  2717. }
  2718. table.entries().forEach(function (col) {
  2719. if (!added[col.catalogEntry.name] && col.catalogEntry.name.toLowerCase().indexOf(partialLower) === 0) {
  2720. added[col.catalogEntry.name] = true;
  2721. result.push(nonPartial + partial + col.catalogEntry.name.substring(partial.length))
  2722. }
  2723. })
  2724. });
  2725. return result;
  2726. };
  2727. });
  2728. self.disposals.push(function () {
  2729. activeDashboardCollection.remove();
  2730. });
  2731. }
  2732. DashboardAssistantPanel.prototype.dispose = function () {
  2733. var self = this;
  2734. self.disposals.forEach(function (dispose) {
  2735. dispose();
  2736. })
  2737. };
  2738. ko.components.register('dashboard-assistant-panel', {
  2739. viewModel: DashboardAssistantPanel,
  2740. template: { element: 'editor-assistant-panel-template' }
  2741. });
  2742. })();
  2743. </script>
  2744. <script type="text/html" id="right-assist-panel-template">
  2745. <div style="height: 100%; width: 100%; position: relative;">
  2746. <ul class="right-panel-tabs nav nav-pills">
  2747. <li data-bind="css: { 'active' : activeTab() === 'editorAssistant' }, visible: editorAssistantTabAvailable" style="display:none;"><a href="javascript: void(0);" data-bind="click: function() { lastActiveTabEditor('editorAssistant'); activeTab('editorAssistant'); }">${ _('Assistant') }</a></li>
  2748. <li data-bind="css: { 'active' : activeTab() === 'functions' }, visible: functionsTabAvailable" style="display:none;"><a href="javascript: void(0);" data-bind="click: function() { lastActiveTabEditor('functions'); activeTab('functions'); }">${ _('Functions') }</a></li>
  2749. <li data-bind="css: { 'active' : activeTab() === 'schedules' }, visible: schedulesTabAvailable" style="display:none;"><a href="javascript: void(0);" data-bind="click: function() { lastActiveTabEditor('schedules'); activeTab('schedules'); }">${ _('Schedule') }</a></li>
  2750. <li data-bind="css: { 'active' : activeTab() === 'dashboardAssistant' }, visible: dashboardAssistantTabAvailable" style="display:none;"><a href="javascript: void(0);" data-bind="click: function() { lastActiveTabDashboard('dashboardAssistant'); activeTab('dashboardAssistant'); }">${ _('Assistant') }</a></li>
  2751. </ul>
  2752. <div class="right-panel-tab-content tab-content">
  2753. <!-- ko if: editorAssistantTabAvailable-->
  2754. <div data-bind="component: { name: 'editor-assistant-panel', params: { activeTab: activeTab } }, visible: activeTab() === 'editorAssistant'"></div>
  2755. <!-- /ko -->
  2756. <!-- ko if: functionsTabAvailable -->
  2757. <div data-bind="component: { name: 'functions-panel' }, visible: activeTab() === 'functions'"></div>
  2758. <!-- /ko -->
  2759. <!-- ko if: dashboardAssistantTabAvailable -->
  2760. <div data-bind="component: { name: 'dashboard-assistant-panel' }, visible: activeTab() === 'dashboardAssistant'"></div>
  2761. <!-- /ko -->
  2762. ## TODO: Switch to if: when loadSchedules from notebook.ko.js has been moved to the schedule-panel component
  2763. <div data-bind="component: { name: 'schedule-panel' }, visible: activeTab() === 'schedules'" style="display:none;"></div>
  2764. </div>
  2765. </div>
  2766. </script>
  2767. <script type="text/javascript">
  2768. (function () {
  2769. var EDITOR_ASSISTANT_TAB = 'editorAssistant';
  2770. var DASHBOARD_ASSISTANT_TAB = 'dashboardAssistant';
  2771. var FUNCTIONS_TAB = 'functions';
  2772. var SCHEDULES_TAB = 'schedules';
  2773. function RightAssistPanel(params) {
  2774. var self = this;
  2775. self.disposals = [];
  2776. self.activeTab = ko.observable();
  2777. self.editorAssistantTabAvailable = ko.observable(false);
  2778. self.dashboardAssistantTabAvailable = ko.observable(false);
  2779. self.functionsTabAvailable = ko.observable(false);
  2780. self.schedulesTabAvailable = ko.observable(false);
  2781. var apiHelper = ApiHelper.getInstance();
  2782. self.lastActiveTabEditor = apiHelper.withTotalStorage('assist', 'last.open.right.panel', ko.observable(), EDITOR_ASSISTANT_TAB);
  2783. self.lastActiveTabDashboard = apiHelper.withTotalStorage('assist', 'last.open.right.panel.dashboard', ko.observable(), DASHBOARD_ASSISTANT_TAB);
  2784. var assistEnabledApp = false;
  2785. huePubSub.subscribe('assist.highlight.risk.suggestions', function () {
  2786. if (self.editorAssistantTabAvailable() && self.activeTab() !== EDITOR_ASSISTANT_TAB) {
  2787. self.activeTab(EDITOR_ASSISTANT_TAB);
  2788. }
  2789. });
  2790. var updateTabs = function () {
  2791. if (!assistEnabledApp) {
  2792. params.rightAssistAvailable(false);
  2793. return;
  2794. }
  2795. var rightAssistAvailable = true;
  2796. if (self.lastActiveTabEditor() === FUNCTIONS_TAB && self.functionsTabAvailable()) {
  2797. self.activeTab(FUNCTIONS_TAB);
  2798. } else if (self.lastActiveTabEditor() === SCHEDULES_TAB && self.schedulesTabAvailable()) {
  2799. self.activeTab(SCHEDULES_TAB);
  2800. } else if (self.editorAssistantTabAvailable()) {
  2801. self.activeTab(EDITOR_ASSISTANT_TAB);
  2802. } else if (self.functionsTabAvailable()) {
  2803. self.activeTab(FUNCTIONS_TAB);
  2804. } else if (self.schedulesTabAvailable()) {
  2805. self.activeTab(SCHEDULES_TAB);
  2806. } else if (self.dashboardAssistantTabAvailable()) {
  2807. self.activeTab(DASHBOARD_ASSISTANT_TAB);
  2808. } else {
  2809. self.activeTab(null);
  2810. rightAssistAvailable = false;
  2811. }
  2812. params.rightAssistAvailable(rightAssistAvailable);
  2813. };
  2814. var updateContentsForType = function (type) {
  2815. self.functionsTabAvailable(type === 'hive' || type === 'impala' || type === 'pig');
  2816. self.editorAssistantTabAvailable(type === 'hive' || type === 'impala');
  2817. self.dashboardAssistantTabAvailable(type === 'dashboard');
  2818. self.schedulesTabAvailable(false);
  2819. if (type !== 'dashboard') {
  2820. if ('${ ENABLE_QUERY_SCHEDULING.get() }' === 'True' && IS_HUE_4) {
  2821. huePubSub.subscribeOnce('set.current.app.view.model', function (viewModel) {
  2822. // Async
  2823. self.schedulesTabAvailable(!!viewModel.selectedNotebook);
  2824. updateTabs();
  2825. });
  2826. huePubSub.publish('get.current.app.view.model');
  2827. } else {
  2828. // Right assist is only available in the Hue 3 editor and notebook.
  2829. self.schedulesTabAvailable('${ ENABLE_QUERY_SCHEDULING.get() }' === 'True');
  2830. }
  2831. }
  2832. updateTabs();
  2833. };
  2834. var snippetTypeSub = huePubSub.subscribe('active.snippet.type.changed', updateContentsForType);
  2835. self.disposals.push(snippetTypeSub.remove.bind(snippetTypeSub));
  2836. if (IS_HUE_4) {
  2837. huePubSub.subscribe('set.current.app.name', function (appName) {
  2838. assistEnabledApp = appName === 'editor' || appName === 'notebook' || appName === 'dashboard';
  2839. if (!assistEnabledApp) {
  2840. params.rightAssistAvailable(false);
  2841. }
  2842. if (appName === 'dashboard') {
  2843. updateContentsForType(appName);
  2844. }
  2845. });
  2846. huePubSub.publish('get.current.app.name');
  2847. } else {
  2848. assistEnabledApp = true;
  2849. }
  2850. updateTabs();
  2851. }
  2852. RightAssistPanel.prototype.dispose = function () {
  2853. var self = this;
  2854. self.disposals.forEach(function (dispose) {
  2855. dispose();
  2856. })
  2857. };
  2858. ko.components.register('right-assist-panel', {
  2859. viewModel: RightAssistPanel,
  2860. template: { element: 'right-assist-panel-template' }
  2861. });
  2862. })();
  2863. </script>
  2864. </%def>