UNPKG

gethue

Version:

Hue is an Open source SQL Query Editor for Databases/Warehouses

433 lines (408 loc) 17.7 kB
// Licensed to Cloudera, Inc. under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. Cloudera, Inc. licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import $ from 'jquery'; import * as ko from 'knockout'; import { ASSIST_DOC_HIGHLIGHT_EVENT, ASSIST_SHOW_DOC_EVENT } from './events'; import { DOCUMENT_TYPES } from '../../../doc/docSupport'; import HueFileEntry from '../../../doc/hueFileEntry'; import componentUtils from '../../../ko/components/componentUtils'; import { CONFIG_REFRESHED_TOPIC } from '../../../config/events'; import { getLastKnownConfig } from '../../../config/hueConfig'; import huePubSub from '../../../utils/huePubSub'; import I18n from '../../../utils/i18n'; import { getFromLocalStorage, setInLocalStorage } from '../../../utils/storageUtils'; export const REFRESH_DOC_ASSIST_EVENT = 'assist.document.refresh'; // prettier-ignore const TEMPLATE = ` <script type="text/html" id="document-context-items"> <!-- ko if: definition().type === 'directory' --> <li><a href="javascript: void(0);" data-bind="click: open"><i class="fa fa-fw fa-folder-open-o"></i> ${I18n( 'Open folder' )}</a></li> <!-- /ko --> <!-- ko if: definition().type !== 'directory' --> <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> ${I18n( 'Show details' )}</a></li> <li><a href="javascript: void(0);" data-bind="click: open"><i class="fa fa-fw fa-edit"></i> ${I18n( 'Open document' )}</a></li> <li><a href="javascript: void(0);" data-bind="click: function() { $data.selected(true); huePubSub.publish('doc.show.delete.modal', $data.parent); }"><i class="fa fa-fw fa-trash-o"></i> ${I18n( 'Delete document' )}</a></li> <!-- /ko --> <!-- ko if: $containerContext.sharingEnabled --> <li><a href="javascript: void(0);" data-bind="publish: { 'doc.show.share.modal': $data }"><i class="fa fa-fw fa-users"></i> ${I18n( 'Share' )}</a></li> <!-- /ko --> <li><a href="javascript: void(0);" data-bind="click: contextMenuDownload"><i class="fa fa-fw fa-download"></i> ${I18n( 'Download' )}</a></li> </script> <script type="text/html" id="assist-document-header-actions"> <div class="assist-db-header-actions"> <!-- ko if: !loading() --> <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: '${I18n( 'Document type' )}' } }" style="display: inline-block"></div> <!-- /ko --> <span class="dropdown new-document-drop-down"> <a class="inactive-action dropdown-toggle" data-toggle="dropdown" data-bind="dropdown" href="javascript:void(0);"> <i class="pointer fa fa-plus" title="${I18n('New document')}"></i> </a> <ul class="dropdown-menu less-padding document-types" style="margin-top:3px; margin-left:-140px; width: 175px;position: absolute;" role="menu"> <!-- ko if: window.HUE_APPS.indexOf('beeswax') !== -1 --> <li> <a title="${I18n( window.ENABLE_UNIFIED_ANALYTICS ? 'Unified Analytics Query' : 'Hive Query' )}" data-bind="click: function() { huePubSub.publish('open.editor.new.query', {type: 'hive', 'directoryUuid': $data.getDirectory()}); }" href="javascript:void(0);"> <!-- ko template: { name: 'app-icon-template', data: { icon: window.ENABLE_UNIFIED_ANALYTICS ? 'impala' : 'hive' } } --><!-- /ko --> ${I18n( window.ENABLE_UNIFIED_ANALYTICS ? 'Unified Analytics' : 'Hive' )} </a> </li> <!-- /ko --> <!-- ko if: window.HUE_APPS.indexOf('impala') !== -1 --> <li> <a title="${I18n( 'Impala Query' )}" class="dropdown-item" data-bind="click: function() { huePubSub.publish('open.editor.new.query', {type: 'impala', 'directoryUuid': $data.getDirectory()}); }" href="javascript:void(0);"> <!-- ko template: { name: 'app-icon-template', data: { icon: 'impala' } } --><!-- /ko --> ${I18n( 'Impala' )} </a> </li> <!-- /ko --> <!-- ko if: window.SHOW_NOTEBOOKS --> <li> <a title="${I18n( 'Notebook' )}" data-bind="click: function() { $('.new-document-drop-down').removeClass('open');}, hueLink: $data.addDirectoryParamToUrl(window.HUE_URLS.NOTEBOOK_INDEX)"> <!-- ko template: { name: 'app-icon-template', data: { icon: 'notebook' } } --><!-- /ko --> ${I18n( 'Notebook' )} </a> </li> <!-- /ko --> <!-- ko if: window.HUE_APPS.indexOf('pig') !== -1 --> <li> <a title="${I18n( 'Pig Script' )}" data-bind="click: function() { $('.new-document-drop-down').removeClass('open');}, hueLink: $data.addDirectoryParamToUrl(window.HUE_URLS.PIG_INDEX)"> <!-- ko template: { name: 'app-icon-template', data: { icon: 'pig' } } --><!-- /ko --> ${I18n( 'Pig Script' )} </a> </li> <!-- /ko --> <!-- ko if: window.HUE_APPS.indexOf('oozie') !== -1 --> <li> <a title="${I18n( 'Oozie Workflow' )}" data-bind="click: function() { $('.new-document-drop-down').removeClass('open');}, hueLink: $data.addDirectoryParamToUrl(window.HUE_URLS.OOZIE_NEW_WORKFLOW)"> <!-- ko template: { name: 'app-icon-template', data: { icon: 'oozie-workflow' } } --><!-- /ko --> ${I18n( 'Workflow' )} </a> </li> <li> <a title="${I18n( 'Oozie Schedule' )}" data-bind="click: function() { $('.new-document-drop-down').removeClass('open');}, hueLink: $data.addDirectoryParamToUrl(window.HUE_URLS.OOZIE_NEW_COORDINATOR)"> <!-- ko template: { name: 'app-icon-template', data: { icon: 'oozie-coordinator' } } --><!-- /ko --> ${I18n( 'Schedule' )} </a> </li> <li> <a title="${I18n( 'Oozie Bundle' )}" data-bind="click: function() { $('.new-document-drop-down').removeClass('open');}, hueLink: $data.addDirectoryParamToUrl(window.HUE_URLS.OOZIE_NEW_BUNDLE)"> <!-- ko template: { name: 'app-icon-template', data: { icon: 'oozie-bundle' } } --><!-- /ko --> ${I18n( 'Bundle' )} </a> </li> <!-- /ko --> <!-- ko if: window.HUE_APPS.indexOf('search') !== -1 --> <li> <a title="${I18n( 'Solr Search' )}" data-bind="click: function() { $('.new-document-drop-down').removeClass('open');}, hueLink: $data.addDirectoryParamToUrl(window.HUE_URLS.SEARCH_NEW_SEARCH)"> <!-- ko template: { name: 'app-icon-template', data: { icon: 'dashboard' } } --><!-- /ko --> ${I18n( 'Dashboard' )} </a> </li> <!-- /ko --> <li class="divider"></li> <li data-bind="css: { 'disabled': $data.isTrash() || $data.isTrashed() || !$data.canModify() }"> <a href="javascript:void(0);" data-bind="click: function () { $('.new-document-drop-down').removeClass('open'); huePubSub.publish('show.create.directory.modal', $data); }"><svg class="hi"><use xlink:href="#hi-folder"></use><use xlink:href="#hi-plus-addon"></use></svg> ${I18n( 'New folder' )}</a> </li> </ul> </span> <a class="inactive-action" href="javascript:void(0)" data-bind="click: function () { huePubSub.publish('${ REFRESH_DOC_ASSIST_EVENT }'); }"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }" title="${I18n( 'Manual refresh' )}"></i></a> </div> </script> <!-- ko with: activeEntry --> <div class="assist-flex-header assist-breadcrumb" style="overflow: visible"> <!-- ko ifnot: isRoot --> <a href="javascript: void(0);" data-bind="click: function () { if (loaded()) { parent.makeActive(); } }"> <i class="fa fa-fw fa-chevron-left"></i> <i class="fa fa-fw fa-folder-o"></i> <span data-bind="text: definition().name, attr: {'title': definition().name }"></span> </a> <!-- /ko --> <!-- ko if: isRoot --> <div> <i class="fa fa-fw fa-folder-o"></i> <span>/</span> </div> <!-- /ko --> <!-- ko template: 'assist-document-header-actions' --><!-- /ko --> </div> <div class="assist-flex-search"> <div class="assist-filter"> <form autocomplete="off"> <input class="clearable" type="text" ${ window.PREVENT_AUTOFILL_INPUT_ATTRS } placeholder="${I18n( 'Filter...' )}" data-bind="clearable: filter, value: filter, valueUpdate: 'afterkeydown'"/> </form> </div> </div> <div class="assist-flex-fill assist-file-scrollable" data-bind="delayedOverflow"> <div data-bind="visible: ! loading() && ! hasErrors() && entries().length > 0"> <!-- ko if: filteredEntries().length == 0 --> <ul class="assist-tables"> <li class="assist-entry"><span class="assist-no-entries">${I18n( 'No documents found' )}</span></li> </ul> <!-- /ko --> <ul class="assist-tables" data-bind="foreachVisible: { data: filteredEntries, minHeight: 27, container: '.assist-file-scrollable' }"> <li class="assist-entry assist-file-entry" data-bind="appAwareTemplateContextMenu: { template: 'document-context-items', containerContext: $parents[2], scrollContainer: '.assist-file-scrollable', beforeOpen: beforeContextOpen }, assistFileDroppable, assistFileDraggable, visibleOnHover: { 'selector': '.assist-file-actions' }"> <div class="assist-file-actions table-actions"> <a class="inactive-action" href="javascript:void(0)" data-bind="popoverOnHover: showContextPopover, css: { 'blue': statsVisible }"><i class="fa fa-fw fa-info" title="${I18n( 'Show details' )}"></i></a> </div> <a href="javascript:void(0)" class="assist-entry assist-document-link" data-bind="click: open, attr: {'title': name }"> <!-- ko template: { name: 'document-icon-template', data: { document: $data, showShareAddon: true } } --><!-- /ko --> <span class="highlightable" data-bind="css: { 'highlight': highlight }, text: definition().name"></span> </a> </li> </ul> </div> <div data-bind="visible: !loading() && ! hasErrors() && entries().length === 0"> <span class="assist-no-entries">${I18n('Empty directory')}</span> </div> <div class="center" data-bind="visible: loading() && ! hasErrors()"> <i class="fa fa-spinner fa-spin" style="font-size: 20px; color: #BBB"></i> </div> <div class="assist-errors" data-bind="visible: ! loading() && hasErrors()"> <span>${I18n('Error loading contents.')}</span> </div> </div> <!-- /ko --> `; class AssistDocumentsPanel { /** * @param {Object} options * @param {string} options.user * @constructor **/ constructor(options) { const self = this; self.user = options.user; self.activeEntry = ko.observable(); self.activeSort = ko.observable('defaultAsc'); self.typeFilter = ko.observable(DOCUMENT_TYPES[0]); // all is first self.sharingEnabled = ko.observable(false); const updateFromConfig = hueConfig => { self.sharingEnabled( hueConfig && (hueConfig.hue_config.is_admin || hueConfig.hue_config.enable_sharing) ); }; updateFromConfig(getLastKnownConfig()); huePubSub.subscribe(CONFIG_REFRESHED_TOPIC, updateFromConfig); self.highlightTypeFilter = ko.observable(false); const lastOpenedUuid = getFromLocalStorage('assist.last.opened.assist.doc.uuid'); if (lastOpenedUuid) { self.activeEntry( new HueFileEntry({ activeEntry: self.activeEntry, trashEntry: ko.observable(), app: 'documents', user: self.user, activeSort: self.activeSort, typeFilter: self.typeFilter, definition: { uuid: lastOpenedUuid, type: 'directory' } }) ); } else { self.fallbackToRoot(); } self.activeEntry.subscribe(newEntry => { if (!newEntry.loaded()) { const loadedSub = newEntry.loaded.subscribe(loaded => { if ( loaded && !newEntry.hasErrors() && newEntry.definition() && newEntry.definition().uuid ) { setInLocalStorage('assist.last.opened.assist.doc.uuid', newEntry.definition().uuid); } loadedSub.dispose(); }); } else if (!newEntry.hasErrors() && newEntry.definition() && newEntry.definition().uuid) { setInLocalStorage('assist.last.opened.assist.doc.uuid', newEntry.definition().uuid); } }); self.reload = () => { self.activeEntry().load( () => {}, () => { self.fallbackToRoot(); } ); }; huePubSub.subscribe(REFRESH_DOC_ASSIST_EVENT, () => { huePubSub.publish('assist.clear.document.cache'); self.reload(); }); huePubSub.subscribe(ASSIST_DOC_HIGHLIGHT_EVENT, details => { huePubSub.publish(ASSIST_SHOW_DOC_EVENT); huePubSub.publish('context.popover.hide'); const whenLoaded = $.Deferred().done(() => { self.activeEntry().highlightInside(details.docUuid); }); if ( self.activeEntry() && self.activeEntry().definition() && self.activeEntry().definition().uuid === details.parentUuid ) { if (self.activeEntry().loaded() && !self.activeEntry().hasErrors()) { whenLoaded.resolve(); } else { const loadedSub = self.activeEntry().loaded.subscribe(newVal => { if (newVal) { if (!self.activeEntry().hasErrors()) { whenLoaded.resolve(); } whenLoaded.reject(); loadedSub.remove(); } }); } self.activeEntry().highlight(details.docUuid); } else { self.activeEntry( new HueFileEntry({ activeEntry: self.activeEntry, trashEntry: ko.observable(), app: 'documents', user: self.user, activeSort: self.activeSort, typeFilter: self.typeFilter, definition: { uuid: details.parentUuid, type: 'directory' } }) ); self.activeEntry().load( () => { whenLoaded.resolve(); }, () => { whenLoaded.reject(); self.fallbackToRoot(); } ); } }); huePubSub.subscribe('assist.documents.set.type.filter', docType => { if (docType) { this.setTypeFilter(docType); } }); self.init(); } setTypeFilter(newType) { const self = this; DOCUMENT_TYPES.some(docType => { if (docType.type === newType) { self.typeFilter(docType); return true; } }); self.highlightTypeFilter(true); window.setTimeout(() => { self.highlightTypeFilter(false); }, 600); } fallbackToRoot() { const self = this; if ( !self.activeEntry() || (self.activeEntry().definition() && (self.activeEntry().definition().path !== '/' || self.activeEntry().definition().uuid)) ) { setInLocalStorage('assist.last.opened.assist.doc.uuid', null); self.activeEntry( new HueFileEntry({ activeEntry: self.activeEntry, trashEntry: ko.observable(), app: 'documents', user: self.user, activeSort: self.activeSort, typeFilter: self.typeFilter, definition: { name: '/', type: 'directory' } }) ); self.activeEntry().load(); } } init() { const self = this; if (!self.activeEntry().loaded()) { self.activeEntry().load( () => {}, () => { self.fallbackToRoot(); }, true ); } } } componentUtils.registerStaticComponent( 'hue-assist-documents-panel', AssistDocumentsPanel, TEMPLATE );