gethue
Version:
Hue is an Open source SQL Query Editor for Databases/Warehouses
237 lines (218 loc) • 11 kB
JavaScript
// 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 componentUtils from '../../../ko/components/componentUtils';
import huePubSub from '../../../utils/huePubSub';
import I18n from '../../../utils/i18n';
import HueFileEntry from '../../../doc/hueFileEntry';
import DisposableComponent from '../../../ko/components/DisposableComponent';
import './ko.linkSharing';
import { REFRESH_DOC_ASSIST_EVENT } from '../../../ko/components/assist/ko.assistDocumentsPanel';
export const SHOW_EVENT = 'doc.show.share.modal';
export const SHOWN_EVENT = 'doc.share.modal.shown';
// prettier-ignore
const TEMPLATE = `
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="${ I18n('Close') }"><span aria-hidden="true">×</span></button>
<h2 class="modal-title">${ I18n('Sharing') } - <span data-bind="text: documentName"></span></h2>
</div>
<div class="modal-body" style="overflow: visible;">
<!-- ko with: document -->
<!-- ko with: definition -->
<!-- ko component: {
name: 'link-sharing',
params: {
document: $parent,
docDefinition: $data
}
} --><!-- /ko -->
<div class="row-fluid" data-bind="visible: !$parent.hasErrors()" id="scrolldiv">
<div class="span6">
<h4 class="muted" style="margin-top: 0">${ I18n('Read') }</h4>
<div data-bind="visible: (perms.read.users.length == 0 && perms.read.groups.length == 0)">${ I18n('The document is not shared for read.') }</div>
<ul class="unstyled airy" style="max-height: 20vh; overflow-x:auto;" data-bind="foreach: perms.read.users">
<li>
<span class="badge badge-info" data-bind="css: { 'badge-left' : $parents[1].fileEntry.canModify() }"><i class="fa fa-user"></i> <span data-bind="text: $parents[1].prettifyUsernameById(id), attr:{'data-id': id}"></span></span><span class="badge badge-right trash-share" data-bind="visible: $parents[1].fileEntry.canModify(), click: function() { $parents[1].removeUserReadShare($data) }"> <i class="fa fa-times"></i></span>
</li>
</ul>
<ul class="unstyled airy" style="max-height: 20vh; overflow-x:auto;" data-bind="foreach: perms.read.groups">
<li>
<span class="badge badge-info" data-bind="css: { 'badge-left' : $parents[1].fileEntry.canModify() }"><i class="fa fa-users"></i> ${ I18n('Group') } "<span data-bind="text: name"></span>"</span><span class="badge badge-right trash-share" data-bind="visible: $parents[1].fileEntry.canModify(), click: function() { $parents[1].removeGroupReadShare($data) }"> <i class="fa fa-times"></i></span>
</li>
</ul>
</div>
<div class="span6">
<h4 class="muted" style="margin-top: 0">${ I18n('Modify') }</h4>
<div data-bind="visible: (perms.write.users.length == 0 && perms.write.groups.length == 0)">${ I18n('The document is not shared for modify.') }</div>
<ul class="unstyled airy" style="max-height: 20vh; overflow-x:auto;" data-bind="foreach: perms.write.users">
<li>
<span class="badge badge-info badge-left" data-bind="css: { 'badge-left' : $parents[1].fileEntry.canModify() }">
<i class="fa fa-user">
</i>
<span data-bind="text: $parents[1].prettifyUsernameById(id), attr:{'data-id': id}">
</span>
</span>
<span class="badge badge-right trash-share" data-bind="visible: $parents[1].fileEntry.canModify(), click: function() { $parents[1].removeUserWriteShare($data) }">
<i class="fa fa-times"></i>
</span>
</li>
</ul>
<ul class="unstyled airy" style="max-height: 20vh; overflow-x:auto;" data-bind="foreach: perms.write.groups">
<li>
<span class="badge badge-info badge-left" data-bind="css: { 'badge-left' : $parents[1].fileEntry.canModify() }">
<i class="fa fa-users"></i> ${ I18n('Group') } "
<span data-bind="text: name"></span>"</span>
<span class="badge badge-right trash-share" data-bind="visible: $parents[1].fileEntry.canModify(), click: function() { $parents[1].removeGroupWriteShare($data) }">
<i class="fa fa-times"></i>
</span>
</li>
</ul>
</div>
</div>
<!-- /ko -->
<div class="doc-browser-empty animated" style="display: none;" data-bind="visible: loading">
<i class="fa fa-spinner fa-spin fa-2x" style="color: #999;"></i>
</div>
<div class="doc-browser-empty animated" style="display: none;" data-bind="visible: hasErrors() && ! loading()">
${ I18n('There was an error loading the document.')}
</div>
<div style="margin-top: 20px" data-bind="visible: fileEntry.canModify() && ! hasErrors() && ! loading()">
<div id="shareDocAutocompleteMenu"></div>
<div class="input-append" style="font-size: inherit">
<input id="shareDocUserInput" placeholder="${ I18n('Type a username or a group name') }" type="text" data-bind="
autocomplete: {
source: shareAutocompleteUserSource.bind($data),
select: function(event,ui) { onShareAutocompleteSelectEnter(event,ui);},
itemTemplate: 'user-search-autocomp-item',
noMatchTemplate: 'user-search-autocomp-no-match',
valueObservable: searchInput,
showSpinner: true,
classPrefix: 'hue-',
onEnter: onShareAutocompleteUserEnter.bind($data),
appendTo: '#shareDocAutocompleteMenu'
},
clearable: {
value: searchInput,
textInput: searchInput
}
" class="ui-autocomplete-input" autocomplete="off" style="width: 476px">
<div class="btn-group" style="overflow:visible; margin-left: -4px;">
<a id="documentShareAddBtn" class="btn" style="border-radius: 0" data-bind="click: function () { onShareAutocompleteUserEnter() }">
<span data-bind="text: selectedPerm() == 'read' ? '${ I18n('Read') }' : '${ I18n('Modify') }'">
</span>
</a>
<a id="documentShareCaret" class="btn dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>
<a data-bind="click: function () { selectedPerm('read'); onShareAutocompleteUserEnter() }" href="javascript:void(0)">
<i class="fa fa-plus"></i> ${ I18n('Read') }
</a>
</li>
<li>
<a data-bind="click: function () { selectedPerm('write'); onShareAutocompleteUserEnter() }" href="javascript:void(0)">
<i class="fa fa-plus"></i> ${ I18n('Modify') }
</a>
</li>
</ul>
</div>
</div>
</div>
<!-- /ko -->
</div>
<div class="modal-footer">
<a href="#" data-dismiss="modal" class="btn disable-feedback disable-enter">${ I18n('Close') }</a>
</div>
`;
class ShareDocModal extends DisposableComponent {
constructor(params) {
super();
this.document = ko.observable();
this.perms = ko.observable();
this.uuid = ko.observable();
this.wasSharedBefore = undefined;
this.documentName = ko.pureComputed(() => this.document() && this.document().definition().name);
this.showLinkSharing = ko.pureComputed(
() => window.HAS_LINK_SHARING && this.perms() && this.perms().link_sharing_on
);
this.initDocument(params);
}
dispose() {
if (this.wasSharedBefore !== this.document().isShared()) {
huePubSub.publish(REFRESH_DOC_ASSIST_EVENT);
}
}
async initDocument(params) {
try {
const document = await getDocument(params);
this.wasSharedBefore = document.isShared();
this.document(document);
this.perms(document.definition().perms);
this.uuid(document.definition().uuid);
} catch (err) {
console.warn('Failed loading document.');
console.error(err);
}
}
}
const getDocument = async params =>
new Promise(async (resolve, reject) => {
const ensureLoaded = async entry => entry.document() || (await entry.loadDocument());
if (typeof params === 'string') {
const entry = new HueFileEntry({
activeEntry: ko.observable(),
trashEntry: ko.observable(),
app: 'documents',
activeSort: ko.observable(),
user: window.LOGGED_USERNAME,
definition: {
uuid: params
}
});
entry.load(async () => {
resolve(await ensureLoaded(entry));
}, reject);
} else {
resolve(await ensureLoaded(params));
}
});
componentUtils.registerComponent('share-doc-modal', ShareDocModal, TEMPLATE).then(() => {
huePubSub.subscribe(SHOW_EVENT, async params => {
let $shareDocMocal = $('#shareDocModal');
if ($shareDocMocal.length) {
ko.cleanNode($shareDocMocal[0]);
$shareDocMocal.remove();
}
$shareDocMocal = $(
'<div id="shareDocModal" data-bind="descendantsComplete: descendantsComplete, component: { name: \'share-doc-modal\', params: params }" data-keyboard="true" class="modal hide fade" tabindex="-1"></div>'
);
$('body').append($shareDocMocal);
const data = {
params: params,
descendantsComplete: () => {
huePubSub.publish(SHOWN_EVENT);
}
};
ko.applyBindings(data, $shareDocMocal[0]);
$shareDocMocal.modal('show');
$shareDocMocal.on('hide', () => {
ko.cleanNode($shareDocMocal[0]);
$shareDocMocal.remove();
});
});
});