assist.mako 174 KB

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