diff --git a/dashboardv2/gruntfile.js b/dashboardv2/gruntfile.js index fc8fa0d..2bcecfe 100644 --- a/dashboardv2/gruntfile.js +++ b/dashboardv2/gruntfile.js @@ -98,14 +98,15 @@ module.exports = function(grunt) { 'select2.full.min.js': { 'select2/dist/js': 'select2' }, 'backgrid-select-all.min.js': { 'backgrid-select-all': 'backgrid-select-all' }, 'moment.min.js': { 'moment/min': 'moment/js' }, - 'moment-timezone-with-data.min.js' : {'moment-timezone/builds' : 'moment-timezone'}, + 'moment-timezone-with-data.min.js': { 'moment-timezone/builds': 'moment-timezone' }, 'jquery.placeholder.js': { 'jquery-placeholder': 'jquery-placeholder/js' }, 'platform.js': { 'platform': 'platform' }, 'query-builder.standalone.min.js': { 'jQuery-QueryBuilder/dist/js': 'jQueryQueryBuilder/js' }, 'daterangepicker.js': { 'bootstrap-daterangepicker': 'bootstrap-daterangepicker/js' }, 'nv.d3.min.js': { 'nvd3/build': 'nvd3' }, 'jquery.sparkline.min.js': { 'jquery-sparkline': 'sparkline' }, - 'table-dragger.js': { 'table-dragger/dist': 'table-dragger' } + 'table-dragger.js': { 'table-dragger/dist': 'table-dragger' }, + 'jstree.min.js': { 'jstree/dist': 'jstree' } } }, @@ -127,16 +128,22 @@ module.exports = function(grunt) { 'select2.min.css': { 'select2/dist/css': 'select2/css' }, 'backgrid-select-all.min.css': { 'backgrid-select-all': 'backgrid-select-all' }, 'font-awesome.min.css': { 'font-awesome/css': 'font-awesome/css' }, - '*': { + '*': [{ 'expand': true, 'dot': true, 'cwd': nodeModulePath + 'font-awesome', 'src': ['fonts/*.*'], 'dest': libPath + 'font-awesome/' - }, + }, { + 'expand': true, + 'dot': true, + 'cwd': nodeModulePath + 'jstree/dist/themes/', + 'src': ['**'], + 'dest': libPath + 'jstree/css/' + }], 'query-builder.default.min.css': { 'jQuery-QueryBuilder/dist/css': 'jQueryQueryBuilder/css' }, 'daterangepicker.css': { 'bootstrap-daterangepicker': 'bootstrap-daterangepicker/css' }, - 'nv.d3.min.css': { 'nvd3/build': 'nvd3/css' } + 'nv.d3.min.css': { 'nvd3/build': 'nvd3/css' }, } }, @@ -160,7 +167,7 @@ module.exports = function(grunt) { { 'dagre-d3': 'dagre-d3' }, { 'platform': 'platform/' }, { 'jQuery-QueryBuilder': 'jQueryQueryBuilder/' }, - {'moment-timezone' : 'moment-timezone'} + { 'moment-timezone': 'moment-timezone' } ], 'LICENSE.md': [{ 'backbone.babysitter': 'backbone-babysitter' }, { 'backbone.wreqr': 'backbone-wreqr' }, @@ -179,6 +186,14 @@ module.exports = function(grunt) { } } }, + rename: { + main: { + files: [ + { src: [libPath + '/jstree/css/default/style.min.css'], dest: libPath + '/jstree/css/default/default-theme.min.css' }, + { src: [libPath + '/jstree/css/default-dark/style.min.css'], dest: libPath + '/jstree/css/default-dark/default-dark-theme.min.css' }, + ] + } + }, sass: { dist: { files: { @@ -296,8 +311,12 @@ module.exports = function(grunt) { }); } } else { - key = Object.keys(obj); - options.libFiles.push({ 'src': pathPrefix.srcPrefix + key + "/" + fileName, 'dest': pathPrefix.destPrefix + obj[key] + "/" + fileName }); + if (fileName == "*") { + options.libFiles.push(obj); + } else { + key = Object.keys(obj); + options.libFiles.push({ 'src': pathPrefix.srcPrefix + key + "/" + fileName, 'dest': pathPrefix.destPrefix + obj[key] + "/" + fileName }); + } } }; @@ -305,16 +324,12 @@ module.exports = function(grunt) { var options = npmCopy[key].options, files = npmCopy[key].files; for (var fileName in files) { - if (fileName == "*") { - libFiles.push(files[fileName]); - } else { - createPath({ - 'obj': files[fileName], - 'libFiles': libFiles, - 'pathPrefix': options, - 'fileName': fileName - }); - } + createPath({ + 'obj': files[fileName], + 'libFiles': libFiles, + 'pathPrefix': options, + 'fileName': fileName + }); } }; grunt.config.set('copy.libs', { files: libFiles }); @@ -326,6 +341,7 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-htmlmin'); grunt.loadNpmTasks('grunt-template'); + grunt.loadNpmTasks('grunt-contrib-rename'); require('load-grunt-tasks')(grunt); @@ -333,6 +349,7 @@ module.exports = function(grunt) { 'clean', 'copy:libs', 'copy:dist', + 'rename', 'sass:dist', 'template', 'setupProxies:server', @@ -344,6 +361,7 @@ module.exports = function(grunt) { 'clean', 'copy:libs', 'copy:build', + 'rename', 'sass:build', 'template' ]); @@ -352,6 +370,7 @@ module.exports = function(grunt) { 'clean', 'copy:libs', 'copy:dist', + 'rename', 'sass:dist', 'uglify', 'cssmin', @@ -365,6 +384,7 @@ module.exports = function(grunt) { 'clean', 'copy:libs', 'copy:build', + 'rename', 'sass:build', 'uglify', 'cssmin', diff --git a/dashboardv2/package.json b/dashboardv2/package.json index 1f28c4b..e543468 100644 --- a/dashboardv2/package.json +++ b/dashboardv2/package.json @@ -37,8 +37,10 @@ "jquery-asBreadcrumbs": "0.2.2", "jquery-placeholder": "2.3.1", "jquery-sparkline": "2.4.0", + "jstree": "^3.3.5", "moment": "2.21.0", "moment-timezone": "0.5.14", + "npm": "^5.8.0", "nvd3": "1.8.5", "platform": "1.3.4", "pnotify": "3.2.0", @@ -56,6 +58,7 @@ "grunt-contrib-copy": "1.0.0", "grunt-contrib-cssmin": "2.0.0", "grunt-contrib-htmlmin": "2.2.0", + "grunt-contrib-rename": "^0.2.0", "grunt-contrib-uglify": "2.1.0", "grunt-contrib-watch": "1.0.0", "grunt-middleware-proxy": "1.0.7", diff --git a/dashboardv2/public/css/scss/glossary.scss b/dashboardv2/public/css/scss/glossary.scss new file mode 100644 index 0000000..54210c0 --- /dev/null +++ b/dashboardv2/public/css/scss/glossary.scss @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + */ + +.jstree-contextmenu { + z-index: 99; +} + +.jstree-default-dark { + background: transparent !important; + .jstree-clicked, + .jstree-wholerow-clicked { + background: $color_jungle_green_approx !important; + } + .jstree-hovered { + background: $color_star_dust_approx; + } + .jstree-anchor { + color: #dbdbdb; + } + .jstree-search { + color: #fbfece !important + } + li[role="treeitem"] { + .jstree-wholerow-clicked { + .tools { + display: inline-block; + } + } + .tools { + display: none; + position: relative; + width: 9%; + float: right; + text-align: center; + cursor: pointer; + } + } +} \ No newline at end of file diff --git a/dashboardv2/public/css/scss/search.scss b/dashboardv2/public/css/scss/search.scss index 9500558..0f9cb1c 100644 --- a/dashboardv2/public/css/scss/search.scss +++ b/dashboardv2/public/css/scss/search.scss @@ -94,11 +94,11 @@ $color_celeste_approx: #1D1F2B; } } -.popup-tag { +.popup-tag-term { display: none; } -.popover-tag { +.popover-tag-term { .btn { display: block; } @@ -112,7 +112,8 @@ $color_celeste_approx: #1D1F2B; } } } -.table-responsive{ + +.table-responsive { position: relative; } diff --git a/dashboardv2/public/css/scss/style.scss b/dashboardv2/public/css/scss/style.scss index ee33f48..2b226cd 100644 --- a/dashboardv2/public/css/scss/style.scss +++ b/dashboardv2/public/css/scss/style.scss @@ -31,4 +31,5 @@ @import "tag.scss"; @import "search.scss"; @import "profile-table.scss"; +@import "glossary.scss"; @import "override.scss"; \ No newline at end of file diff --git a/dashboardv2/public/index.html.tpl b/dashboardv2/public/index.html.tpl index 553ca1b..8c67cb4 100644 --- a/dashboardv2/public/index.html.tpl +++ b/dashboardv2/public/index.html.tpl @@ -54,6 +54,8 @@ <link href="js/libs/jQueryQueryBuilder/css/query-builder.default.min.css?bust=<%- bust %>" rel="stylesheet"> <link href="js/libs/bootstrap-daterangepicker/css/daterangepicker.css?bust=<%- bust %>" rel="stylesheet"> <link rel="stylesheet" href="js/libs/nvd3/css/nv.d3.min.css?bust=<%- bust %>"> + <link href="js/libs/jstree/css/default-dark/default-dark-theme.min.css?bust=<%- bust %>" rel="stylesheet"> + <link href="js/libs/jstree/css/default/default-theme.min.css?bust=<%- bust %>" rel="stylesheet"> <link href="css/style.css?bust=<%- bust %>" rel="stylesheet"> </head> diff --git a/dashboardv2/public/js/collection/VGlossaryList.js b/dashboardv2/public/js/collection/VGlossaryList.js new file mode 100644 index 0000000..9c4918f --- /dev/null +++ b/dashboardv2/public/js/collection/VGlossaryList.js @@ -0,0 +1,72 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + */ + +define(['require', + 'utils/Globals', + 'collection/BaseCollection', + 'models/VGlossary', + 'utils/UrlLinks' +], function(require, Globals, BaseCollection, VGlossary, UrlLinks) { + 'use strict'; + var VGlossaryList = BaseCollection.extend( + //Prototypal attributes + { + url: UrlLinks.glossaryApiUrl(), + + model: VGlossary, + + initialize: function() { + this.modelName = 'VGlossary'; + this.modelAttrName = ''; + }, + parseRecords: function(resp, options) { + if (_.isEmpty(this.modelAttrName)) { + return resp; + } else { + return resp[this.modelAttrName] + } + }, + getCategory: function(options) { + var url = UrlLinks.categoryApiUrl({ "guid": options.guid, "related": options.related }), + apiOptions = _.extend({ + contentType: 'application/json', + dataType: 'json' + }, options.ajaxOptions); + return this.constructor.nonCrudOperation.call(this, url, 'GET', apiOptions); + }, + getTerm: function(options) { + var url = UrlLinks.termApiUrl({ "guid": options.guid, "related": options.related }), + apiOptions = _.extend({ + contentType: 'application/json', + dataType: 'json' + }, options.ajaxOptions); + return this.constructor.nonCrudOperation.call(this, url, 'GET', apiOptions); + } + }, + //Static Class Members + { + /** + * Table Cols to be passed to Backgrid + * UI has to use this as base and extend this. + * + */ + tableCols: {} + } + ); + return VGlossaryList; +}); \ No newline at end of file diff --git a/dashboardv2/public/js/collection/VSearchList.js b/dashboardv2/public/js/collection/VSearchList.js index 28c246f..db380f6 100644 --- a/dashboardv2/public/js/collection/VSearchList.js +++ b/dashboardv2/public/js/collection/VSearchList.js @@ -53,9 +53,11 @@ define(['require', entities.push(temp); }); return entities; - } else { + } else if (resp.entities) { this.dynamicTable = false; return resp.entities ? resp.entities : []; + } else { + return resp ? resp : []; } }, getBasicRearchResult: function(options) { diff --git a/dashboardv2/public/js/main.js b/dashboardv2/public/js/main.js index a0e4bc5..6c72cca 100644 --- a/dashboardv2/public/js/main.js +++ b/dashboardv2/public/js/main.js @@ -114,11 +114,11 @@ require.config({ 'daterangepicker': { 'deps': ['jquery', 'moment'] }, - 'moment-timezone' : { - 'deps' : ['moment'] + 'moment-timezone': { + 'deps': ['moment'] }, - 'moment':{ - 'exports':['moment'] + 'moment': { + 'exports': ['moment'] }, 'nvd3': { 'deps': ['d3'] @@ -126,6 +126,9 @@ require.config({ 'sparkline': { 'deps': ['jquery'], 'exports': ['sparkline'] + }, + 'jstree': { + 'deps': ['jquery'] } }, @@ -167,7 +170,8 @@ require.config({ 'daterangepicker': 'libs/bootstrap-daterangepicker/js/daterangepicker', 'nvd3': 'libs/nvd3/nv.d3.min', 'sparkline': 'libs/sparkline/jquery.sparkline.min', - 'table-dragger': 'libs/table-dragger/table-dragger' + 'table-dragger': 'libs/table-dragger/table-dragger', + 'jstree': 'libs/jstree/jstree.min' }, /** diff --git a/dashboardv2/public/js/models/VGlossary.js b/dashboardv2/public/js/models/VGlossary.js new file mode 100644 index 0000000..63eb9d2 --- /dev/null +++ b/dashboardv2/public/js/models/VGlossary.js @@ -0,0 +1,111 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + */ + +define(['require', + 'utils/Globals', + 'models/BaseModel', + 'utils/UrlLinks' +], function(require, Globals, VBaseModel, UrlLinks) { + 'use strict'; + var VGlossary = VBaseModel.extend({ + + urlRoot: UrlLinks.glossaryApiUrl(), + + defaults: {}, + + serverSchema: {}, + + idAttribute: 'guid', + + initialize: function() { + this.modelName = 'VGlossary'; + }, + toString: function() { + return this.get('name'); + }, + createEditCategory: function(options) { + var type = "POST", + url = UrlLinks.categoryApiUrl(); + if (options.guid) { + type = "PUT"; + url = UrlLinks.categoryApiUrl({ guid: options.guid }); + } + options = _.extend({ + contentType: 'application/json', + dataType: 'json' + }, options); + return this.constructor.nonCrudOperation.call(this, url, type, options); + }, + createEditTerm: function(options) { + var type = "POST", + url = UrlLinks.termApiUrl(); + if (options.guid) { + type = "PUT"; + url = UrlLinks.termApiUrl({ guid: options.guid }); + } + options = _.extend({ + contentType: 'application/json', + dataType: 'json' + }, options); + return this.constructor.nonCrudOperation.call(this, url, type, options); + }, + deleteCategory: function(guid, options) { + var url = UrlLinks.categoryApiUrl({ "guid": guid }); + options = _.extend({ + contentType: 'application/json', + dataType: 'json' + }, options); + return this.constructor.nonCrudOperation.call(this, url, 'DELETE', options); + }, + deleteTerm: function(guid, options) { + var url = UrlLinks.termApiUrl({ "guid": guid }); + options = _.extend({ + contentType: 'application/json', + dataType: 'json' + }, options); + return this.constructor.nonCrudOperation.call(this, url, 'DELETE', options); + }, + assignTermToEntity: function(guid, options) { + var url = UrlLinks.termToEntityApiUrl(guid); + options = _.extend({ + contentType: 'application/json', + dataType: 'json' + }, options); + return this.constructor.nonCrudOperation.call(this, url, 'POST', options); + }, + assignTermToCategory: function(options) { + return this.createEditCategory(options); + }, + assignCategoryToTerm: function(options) { + return this.createEditTerm(options); + }, + removeTermFromEntity: function(guid, options) { + var url = UrlLinks.termToEntityApiUrl(guid); + options = _.extend({ + contentType: 'application/json', + dataType: 'json' + }, options); + return this.constructor.nonCrudOperation.call(this, url, 'DELETE', options); + }, + removeTermFromCategory: function() { + + }, + removeCategoryFromTerm: function() {} + }, {}); + return VGlossary; +}); \ No newline at end of file diff --git a/dashboardv2/public/js/router/Router.js b/dashboardv2/public/js/router/Router.js index 3d035ad..d4cdf26 100644 --- a/dashboardv2/public/js/router/Router.js +++ b/dashboardv2/public/js/router/Router.js @@ -24,8 +24,8 @@ define([ 'utils/Globals', 'utils/Utils', 'utils/UrlLinks', - 'collection/VTagList' -], function($, _, Backbone, App, Globals, Utils, UrlLinks, VTagList) { + 'collection/VGlossaryList' +], function($, _, Backbone, App, Globals, Utils, UrlLinks, VGlossaryList) { var AppRouter = Backbone.Router.extend({ routes: { // Define some URL routes @@ -36,6 +36,8 @@ define([ '!/detailPage/:id': 'detailPage', '!/tag': 'commonAction', '!/search': 'commonAction', + '!/glossary': 'commonAction', + '!/glossary/:id': 'glossaryDetailPage', // Default '*actions': 'defaultAction' }, @@ -45,14 +47,19 @@ define([ this.bindCommonEvents(); this.listenTo(this, 'route', this.postRouteExecute, this); this.searchVent = new Backbone.Wreqr.EventAggregator(); + this.glossaryCollection = new VGlossaryList([], {}); this.preFetchedCollectionLists = { 'entityDefCollection': this.entityDefCollection, 'typeHeaders': this.typeHeaders, 'enumDefCollection': this.enumDefCollection, - 'classificationDefCollection': this.classificationDefCollection + 'classificationDefCollection': this.classificationDefCollection, + 'glossaryCollection': this.glossaryCollection } this.sharedObj = { searchTableColumns: {}, + glossary: { + selectedItem: {} + }, searchTableFilters: { tagFilters: {}, entityFilters: {} @@ -169,6 +176,31 @@ define([ } }); }, + glossaryDetailPage: function(id) { + var that = this; + if (id) { + require([ + 'views/site/Header', + 'views/glossary/GlossaryDetailLayoutView', + 'views/site/SideNavLayoutView' + ], function(Header, GlossaryDetailLayoutView, SideNavLayoutView) { + var paramObj = Utils.getUrlState.getQueryParams(); + App.rNHeader.show(new Header()); + if (!App.rSideNav.currentView) { + App.rSideNav.show(new SideNavLayoutView( + _.extend({}, that.preFetchedCollectionLists, that.sharedObj, { 'guid': id, 'value': paramObj }) + )); + } else { + App.rSideNav.currentView.RGlossaryLayoutView.currentView.manualRender(_.extend({}, { 'guid': id, 'value': paramObj })); + App.rSideNav.currentView.selectTab(); + } + App.rNContent.show(new GlossaryDetailLayoutView(_.extend({ + 'guid': id, + 'value': paramObj + }, that.preFetchedCollectionLists, that.sharedObj))); + }); + } + }, commonAction: function() { var that = this; require([ @@ -188,8 +220,11 @@ define([ App.rSideNav.currentView.selectTab(); if (Utils.getUrlState.isTagTab()) { App.rSideNav.currentView.RTagLayoutView.currentView.manualRender(); + } else if (Utils.getUrlState.isGlossaryTab()) { + App.rSideNav.currentView.RGlossaryLayoutView.currentView.manualRender(_.extend({}, paramObj)); } } + if (Globals.entityCreate && Utils.getUrlState.isSearchTab()) { App.rNContent.show(new SearchDetailLayoutView( _.extend({ diff --git a/dashboardv2/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html b/dashboardv2/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html index a77ffc1..a23797f 100644 --- a/dashboardv2/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html +++ b/dashboardv2/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html @@ -30,6 +30,14 @@ </button> </div> </div> + <div class="form-group"> + <span class="control-label-sm-pr pull-left">Term:</span> + <div class="pull-left" data-id="termList"> + <button class="btn btn-action btn-sm" title="Add Term" data-id="addTerm"> + <i class="fa fa-plus"> </i> + </button> + </div> + </div> <div class="form-group" style="display: none;" data-id="propagatedTagDiv"> <span class="control-label-sm-pr pull-left">Propagated Classifications:</span> <div class="pull-left" data-id="propagatedTagList"> diff --git a/dashboardv2/public/js/templates/glossary/AssignTermLayoutViewTmpl.html b/dashboardv2/public/js/templates/glossary/AssignTermLayoutViewTmpl.html new file mode 100644 index 0000000..1590a29 --- /dev/null +++ b/dashboardv2/public/js/templates/glossary/AssignTermLayoutViewTmpl.html @@ -0,0 +1,17 @@ +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. +--> +<div id="r_glossaryTree"></div> \ No newline at end of file diff --git a/dashboardv2/public/js/templates/glossary/CreateEditCategoryTermLayoutView_tmpl.html b/dashboardv2/public/js/templates/glossary/CreateEditCategoryTermLayoutView_tmpl.html new file mode 100644 index 0000000..8f17f40 --- /dev/null +++ b/dashboardv2/public/js/templates/glossary/CreateEditCategoryTermLayoutView_tmpl.html @@ -0,0 +1,36 @@ +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. +--> +<form name="tagDefinitionform" class="form-horizontal" data-id="categoryTermForm"> + <div class="form-group"> + <label class="control-label col-sm-2 {{#if create}}required{{/if}}" for="name">Name</label> + <div class="col-sm-10"> + <input class="form-control" data-id="displayName" name="displayName" value="{{modelJSON.displayName}}" placeholder="Name(required)" autofocus/> + </div> + </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="description">Short Description</label> + <div class="col-sm-10"> + <input class="form-control" name="shortDescription" data-id="shortDescription" value="{{modelJSON.shortDescription}}" placeholder="Short Description" /> + </div> + </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="description">Long Description</label> + <div class="col-sm-10"> + <textarea class="form-control" name="longDescription" data-id="longDescription" placeholder="Long Description">{{modelJSON.longDescription}}</textarea> + </div> + </div> +</form> \ No newline at end of file diff --git a/dashboardv2/public/js/templates/glossary/CreateEditGlossaryLayoutView_tmpl.html b/dashboardv2/public/js/templates/glossary/CreateEditGlossaryLayoutView_tmpl.html new file mode 100644 index 0000000..f783706 --- /dev/null +++ b/dashboardv2/public/js/templates/glossary/CreateEditGlossaryLayoutView_tmpl.html @@ -0,0 +1,36 @@ +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. +--> +<form name="tagDefinitionform" class="form-horizontal" data-id="glossaryForm"> + <div class="form-group"> + <label class="control-label col-sm-2 {{#if create}}required{{/if}}" for="name">Name</label> + <div class="col-sm-10"> + <input class="form-control" name="displayName" value="{{displayName}}" data-id="displayName" placeholder="Display Name(required)" autofocus/> + </div> + </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="description">Short Description</label> + <div class="col-sm-10"> + <input class="form-control" name="shortDescription" data-id="shortDescription" value="{{shortDescription}}" placeholder="Short Description" /> + </div> + </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="description">Long Description</label> + <div class="col-sm-10"> + <textarea class="form-control" name="longDescription" data-id="longDescription" placeholder="Long Description">{{longDescription}}</textarea> + </div> + </div> +</form> \ No newline at end of file diff --git a/dashboardv2/public/js/templates/glossary/GlossaryDetailLayoutView_tmpl.html b/dashboardv2/public/js/templates/glossary/GlossaryDetailLayoutView_tmpl.html new file mode 100644 index 0000000..25967ea --- /dev/null +++ b/dashboardv2/public/js/templates/glossary/GlossaryDetailLayoutView_tmpl.html @@ -0,0 +1,67 @@ +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. +--> +<div class="page-title clearfix"> + <div class="fontLoader"> + <i class="fa fa-refresh fa-spin-custom"></i> + </div> + <div data-id="details" class="clearfix form-horizontal col-sm-12"> + <h1 class="row title"><span data-id="title"></span></h1> + <button type="button" data-id="editButton" class="btn btn-sm btn-action pull-right"><i class="fa fa-pencil"></i></button> + <div class="form-group clearfix"> + <span class="pull-left text-muted">Short Description: </span> + <div class="pull-left"> + <p data-id="shortDescription"></p> + </div> + </div> + <div class="form-group clearfix"> + <span class="pull-left text-muted">Long Description: </span> + <div class="pull-left"> + <p data-id="longDescription"></p> + </div> + </div> + {{#if isTermView}} + <div class="form-group clearfix"> + <span class="control-label-sm-pr pull-left">Categories:</span> + <div class="pull-left" data-id="categoryList"> + <button class="btn btn-action btn-sm" title="Add Category" data-id="addCategory"> + <i class="fa fa-plus"> </i> + </button> + </div> + </div> + {{/if}} {{#if isCategoryView}} + <div class="form-group clearfix"> + <span class="control-label-sm-pr pull-left">Term:</span> + <div class="pull-left" data-id="termList"> + <button class="btn btn-action btn-sm" title="Add Term" data-id="addTerm"> + <i class="fa fa-plus"> </i> + </button> + </div> + </div> + {{/if}} + </div> +</div> +<div class="container-fluid gray-bg"> + <div class="row"> + <div class="col-sm-custom"> + <div id="r_searchResultLayoutView"> + <div class="fontLoader" style="display: block;min-height: 50px;position:relative;margin-top: 25px;"> + <i class="fa fa-refresh fa-spin-custom"></i> + </div> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/dashboardv2/public/js/templates/glossary/GlossaryLayoutView_tmpl.html b/dashboardv2/public/js/templates/glossary/GlossaryLayoutView_tmpl.html new file mode 100644 index 0000000..f29f4e7 --- /dev/null +++ b/dashboardv2/public/js/templates/glossary/GlossaryLayoutView_tmpl.html @@ -0,0 +1,52 @@ +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. +--> +<div class="col-sm-12 add-seperator"> + <div class="row"> + {{#unless isAssignView}} + <div class="col-sm-8 no-padding" style="margin-top: 5px;"> + <span class="pull-left">Terms</span> + <label class="switch pull-left"> + <input type="checkbox" class="switch-input" name="glossaryView" value="text" /> + <span class="switch-slider"></span> + </label> + <span class="pull-left">Category</span> + </div> + <div class="{{#if isAssignView}}col-sm-2{{else}}col-sm-4{{/if}} no-padding-right"> + <button type="button" class="btn btn-action btn-md pull-right" title="Refresh" data-id="refreshGlossary" onclick="this.blur();" type="button"><i class="fa fa-refresh"></i></button> + <button type="button" class="btn btn-action btn-md pull-right" data-id="createGlossary" type="button"><i class="fa fa-plus"></i></button> + {{/unless}} + </div> + </div> +</div> +<div class="row"> + <div class="col-sm-12"> + <div class="no-padding col-sm-12 term-view"> + <div> + <input type="text" class="form-control" data-id="searchTerm" placeholder="{{#if isAssignView}}Search Term{{else}}Search Glossary, Term{{/if}}"> + </div> + <div data-id="termTree" style="margin-top: 5px;"> + </div> + </div> + <div class="col-sm-12 no-padding category-view" style="display: none;"> + <div> + <input type="text" class="form-control" data-id="searchCategory" placeholder="{{#if isAssignView}}Search Catalog{{else}}Search Glossary, Category{{/if}}"> + </div> + <div data-id="categoryTree" style="margin-top: 5px;"> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/dashboardv2/public/js/templates/search/SearchResultLayoutView_tmpl.html b/dashboardv2/public/js/templates/search/SearchResultLayoutView_tmpl.html index b7302cd..411aaab 100644 --- a/dashboardv2/public/js/templates/search/SearchResultLayoutView_tmpl.html +++ b/dashboardv2/public/js/templates/search/SearchResultLayoutView_tmpl.html @@ -36,11 +36,13 @@ id="historicalentities"/> <b>Show historical entities</b></label> </div> + {{#ifCond fromView '!==' "glossary"}} <div class="inline" data-id="containerCheckBox" style="display: none;"> <label class="checkbox-inline btn" for="subclassifications"> <input type="checkbox" data-id="checkSubClassification" data-value="excludeSC" id="subclassifications"/> <b>Exclude sub-classifications</b></label> </div> + {{/ifCond}} {{#ifCond fromView '!==' "classification"}} <div class="inline" data-id="containerCheckBox" style="display: none;"> <label class="checkbox-inline btn" for="subtypes"> diff --git a/dashboardv2/public/js/templates/site/SideNavLayoutView_tmpl.html b/dashboardv2/public/js/templates/site/SideNavLayoutView_tmpl.html index 942188e..31ac8e4 100644 --- a/dashboardv2/public/js/templates/site/SideNavLayoutView_tmpl.html +++ b/dashboardv2/public/js/templates/site/SideNavLayoutView_tmpl.html @@ -19,15 +19,19 @@ <a href="{{apiBaseUrl}}/index.html"><i class="fa fa-globe"></i> Apache Atlas</a> </li> <ul class="tabs" style="width: 100%;" role="tablist"> - <li role="presentation" class="{{tabClass}}"><a href="#tab-search" aria-controls="tab-search" data-name="tab-search" role="tab" data-toggle="tab" class=""><i class="fa fa-search"></i> Search</a></li> - <li role="presentation" class="{{tabClass}}"><a href="#tab-tag" aria-controls="tab-tag" data-name="tab-tag" role="tab" data-toggle="tab"><i class="fa fa-tags"></i> Classification</a></li> + <li role="presentation" class="tab col-sm-3"><a href="#tab-search" aria-controls="tab-search" data-name="tab-search" role="tab" data-toggle="tab" class=""><i class="fa fa-search"></i> Search</a></li> + <li role="presentation" class="tab col-sm-5"><a href="#tab-classification" aria-controls="tab-classification" data-name="tab-classification" role="tab" data-toggle="tab"><i class="fa fa-tags"></i> Classification</a></li> + <li role="presentation" class="tab col-sm-4"><a href="#tab-glossary" aria-controls="tab-glossary" data-name="tab-glossary" role="tab" data-toggle="tab"><i class="fa fa-folder-open"></i> Glossary</a></li> </ul> </ul> <div class="tab-content"> - <div role="tabpanel" class="tab-pane" id="tab-tag"> + <div role="tabpanel" class="tab-pane" id="tab-classification"> <div id="r_tagLayoutView"></div> </div> <div role="tabpanel" class="tab-pane" id="tab-search"> <div id="r_searchLayoutView"></div> </div> + <div role="tabpanel" class="tab-pane" id="tab-glossary"> + <div id="r_glossaryLayoutView"></div> + </div> </div> diff --git a/dashboardv2/public/js/utils/CommonViewFunction.js b/dashboardv2/public/js/utils/CommonViewFunction.js index 0e097d4..bc5c170 100644 --- a/dashboardv2/public/js/utils/CommonViewFunction.js +++ b/dashboardv2/public/js/utils/CommonViewFunction.js @@ -233,7 +233,7 @@ define(['require', 'utils/Utils', 'modules/Modal', 'utils/Messages', 'utils/Enum } CommonViewFunction.tagForTable = function(obj) { var traits = obj.classifications, - atags = "", + tagHtml = "", addTag = "", popTag = "", count = 0, @@ -251,7 +251,7 @@ define(['require', 'utils/Utils', 'modules/Modal', 'utils/Messages', 'utils/Enum if (count >= 1) { popTag += tagString; } else { - atags += tagString; + tagHtml += tagString; } ++count; }); @@ -264,9 +264,41 @@ define(['require', 'utils/Utils', 'modules/Modal', 'utils/Messages', 'utils/Enum } } if (count > 1) { - addTag += '<div data-id="showMoreLess" class="btn btn-action btn-sm assignTag"><i class="fa fa-ellipsis-h" aria-hidden="true"></i><div class="popup-tag">' + popTag + '</div></div>' + addTag += '<div data-id="showMoreLess" class="btn btn-action btn-sm assignTag"><i class="fa fa-ellipsis-h" aria-hidden="true"></i><div class="popup-tag-term">' + popTag + '</div></div>' } - return '<div class="tagList btn-inline btn-fixed-width">' + atags + addTag + '</div>'; + return '<div class="tagList btn-inline btn-fixed-width">' + tagHtml + addTag + '</div>'; + } + CommonViewFunction.termForTable = function(obj) { + var terms = obj.meanings, + termHtml = "", + addTerm = "", + popTerm = "", + count = 0, + entityName = Utils.getName(obj); + if (terms) { + terms.map(function(term) { + var className = "btn btn-action btn-sm btn-blue btn-icon", + deleteIcon = '<i class="fa fa-times" data-id="delete" data-assetname="' + entityName + '"data-name="' + term.typeName + '" data-type="tag" data-guid="' + obj.guid + '" data-termGuid="' + term.termGuid + '" ></i>', + termString = '<a class="' + className + '" data-id="termClick"><span title="' + term.typeName + '">' + term.displayText + '</span>' + deleteIcon + '</a>'; + if (count >= 1) { + popTerm += termString; + } else { + termHtml += termString; + } + ++count; + }); + } + if (!Enums.entityStateReadOnly[obj.status]) { + if (obj.guid) { + addTerm += '<a href="javascript:void(0)" data-id="addTerm" class="btn btn-action btn-sm assignTag" data-guid="' + obj.guid + '" ><i class="fa fa-plus"></i></a>'; + } else { + addTerm += '<a href="javascript:void(0)" data-id="addTerm" class="btn btn-action btn-sm assignTag"><i style="right:0" class="fa fa-plus"></i></a>'; + } + } + if (count > 1) { + addTerm += '<div data-id="showMoreLess" class="btn btn-action btn-sm assignTerm"><i class="fa fa-ellipsis-h" aria-hidden="true"></i><div class="popup-tag-term">' + popTerm + '</div></div>' + } + return '<div class="tagList btn-inline btn-fixed-width">' + termHtml + addTerm + '</div>'; } CommonViewFunction.generateQueryOfFilter = function(value) { var entityFilters = CommonViewFunction.attributeFilter.extractUrl({ "value": value.entityFilters, "formatDate": true }), @@ -538,6 +570,183 @@ define(['require', 'utils/Utils', 'modules/Modal', 'utils/Messages', 'utils/Enum } } } + CommonViewFunction.createEditGlossaryCategoryTerm = function(options) { + if (options) { + var model = options.model, + isTermView = options.isTermView, + isGlossaryView = options.isGlossaryView, + collection = options.collection + } + require([ + 'views/glossary/CreateEditCategoryTermLayoutView', + 'views/glossary/CreateEditGlossaryLayoutView', + 'modules/Modal' + ], function(CreateEditCategoryTermLayoutView, CreateEditGlossaryLayoutView, Modal) { + var view = null, + title = null; + if (isGlossaryView) { + view = new CreateEditGlossaryLayoutView({ "glossaryCollection": collection, "model": model }); + title = "Glossary"; + } else { + view = new CreateEditCategoryTermLayoutView({ "glossaryCollection": collection, "modelJSON": model }); + title = (isTermView ? 'Term' : 'Category'); + } + + var modal = new Modal({ + "title": ((model ? "Update " : "Create ") + title), + "content": view, + "cancelText": "Cancel", + "okCloses": false, + "okText": model ? "Update" : "Create", + "allowCancel": true + }).open(); + modal.on('ok', function() { + if (isGlossaryView) { + modal.$el.find('button.ok').attr("disabled", "true"); + } + CommonViewFunction.createEditGlossaryCategoryTermSubmit(_.extend({ "ref": view, "modal": modal }, options)); + }); + modal.on('closeModal', function() { + modal.trigger('cancel'); + if (options.onModalClose) { + options.onModalClose() + } + }); + }); + } + CommonViewFunction.createEditGlossaryCategoryTermSubmit = function(options) { + if (options) { + var ref = options.ref, + modal = options.modal, + model = options.model, + node = options.node, + isTermView = options.isTermView, + isCategoryView = options.isCategoryView, + collection = options.collection, + isGlossaryView = options.isGlossaryView, + data = ref.ui[(isGlossaryView ? "glossaryForm" : "categoryTermForm")].serializeArray().reduce(function(obj, item) { + obj[item.name] = item.value; + return obj; + }, {}), + newModel = new options.collection.model(), + messageType = "Glossary "; + } + if (isTermView) { + messageType = "Term "; + } else if (isCategoryView) { + messageType = "Category "; + } + var ajaxOptions = { + success: function(rModel, response) { + Utils.notifySuccess({ + content: messageType + ref.ui.displayName.val() + Messages[model ? "editSuccessMessage" : "addSuccessMessage"] + }); + if (options.callback) { + options.callback(response); + } + modal.trigger('closeModal'); + } + } + if (model) { + if (isGlossaryView) { + model.set(data).save(null, ajaxOptions) + } else { + newModel[isTermView ? "createEditTerm" : "createEditCategory"](_.extend(ajaxOptions, { + guid: model.guid, + data: JSON.stringify(_.extend({}, model, data)), + })); + } + + } else { + if (isGlossaryView) { + new collection.model().set(data).save(null, ajaxOptions); + } else { + if (node) { + var key = "anchor", + guidKey = "glossaryGuid"; + data["anchor"] = { + "glossaryGuid": node.glossaryId || node.guid, + "displayText": node.glossaryName || node.text + } + if (node.type == "GlossaryCategory") { + data["parentCategory"] = { + "categoryGuid": node.guid + } + } + } + newModel[isTermView ? "createEditTerm" : "createEditCategory"](_.extend(ajaxOptions, { + data: JSON.stringify(data), + })); + } + } + + } + CommonViewFunction.removeCategoryTermAssociation = function(options) { + if (options) { + var selectedGuid = options.guid, + termGuid = options.termGuid, + isCategoryView = options.isCategoryView, + isTermView = options.isTermView, + isEntityView = options.isEntityView, + collection = options.collection, + model = options.model, + newModel = new options.collection.model(), + ajaxOptions = { + success: function(rModel, response) { + Utils.notifySuccess({ + content: ((isCategoryView ? "Term" : "Category") + " association is removed successfully") + }); + if (options.callback) { + options.callback(); + } + modal.trigger('closeModal'); + }, + cust_error: function() { + if (options.hideLoader) { + options.hideLoader(); + } + } + }, + modal = new Modal({ + title: options.titleMessage, + okText: options.buttonText, + htmlContent: options.msg, + cancelText: "Cancel", + allowCancel: true, + okCloses: true, + showFooter: true, + }).open(); + modal.on('ok', function() { + if (options.showLoader) { + options.showLoader(); + } + if (isEntityView && model) { + var data = [model]; + newModel.removeTermFromEntity(termGuid, _.extend(ajaxOptions, { + data: JSON.stringify(data) + })) + } else { + var data = _.extend({}, model); + if (isTermView) { + data.categories = _.reject(data.categories, function(term) { return term.categoryGuid != selectedGuid }); + } else { + data.terms = _.reject(data.terms, function(term) { return term.termGuid != selectedGuid }); + } + + newModel[isTermView ? "createEditTerm" : "createEditCategory"](_.extend(ajaxOptions, { + guid: model.guid, + data: JSON.stringify(_.extend({}, model, data)), + })); + } + }); + modal.on('closeModal', function() { + modal.trigger('cancel'); + if (options.onModalClose) { + options.onModalClose() + } + }); + } + } CommonViewFunction.addRestCsrfCustomHeader = function(xhr, settings) { // if (settings.url == null || !settings.url.startsWith('/webhdfs/')) { if (settings.url == null) { diff --git a/dashboardv2/public/js/utils/Globals.js b/dashboardv2/public/js/utils/Globals.js index 6225715..d9df82a 100644 --- a/dashboardv2/public/js/utils/Globals.js +++ b/dashboardv2/public/js/utils/Globals.js @@ -27,7 +27,8 @@ define(['require'], function(require) { tabState: { stateChanged: false, tagUrl: "#!/tag", - searchUrl: "#!/search" + searchUrl: "#!/search", + glossaryUrl: "#!/glossary" }, detailPageState: {} }; diff --git a/dashboardv2/public/js/utils/Messages.js b/dashboardv2/public/js/utils/Messages.js index 1c733ff..718cbb8 100644 --- a/dashboardv2/public/js/utils/Messages.js +++ b/dashboardv2/public/js/utils/Messages.js @@ -46,7 +46,13 @@ define(['require'], function(require) { tag: { addAttributeSuccessMessage: "Classification attribute is added successfully", updateTagDescriptionMessage: "Classification description is updated successfully" + }, + glossary: { + removeTermfromCategory: "Remove Term Assignment", + removeTermfromEntity: "Remove Term Assignment", + removeCategoryfromTerm: "Remove Category Assignment" } + }; return Messages; }); \ No newline at end of file diff --git a/dashboardv2/public/js/utils/UrlLinks.js b/dashboardv2/public/js/utils/UrlLinks.js index b9f3805..2e5eefe 100644 --- a/dashboardv2/public/js/utils/UrlLinks.js +++ b/dashboardv2/public/js/utils/UrlLinks.js @@ -120,6 +120,51 @@ define(['require', 'utils/Enums', 'utils/Utils', 'underscore'], function(require return saveSearchUrl; } }, + glossaryApiUrl: function(options) { + var guid = options && options.guid, + glossaryUrl = this.baseUrlV2 + '/glossary'; + if (guid) { + return glossaryUrl + '/' + guid; + } else { + return glossaryUrl; + } + }, + categoryApiUrl: function(options) { + var guid = options && options.guid, + list = options && options.list, + related = options && options.related, + categoryUrl = this.glossaryApiUrl() + '/' + (list ? 'categories' : 'category'); + if (guid) { + if (related) { + return categoryUrl + '/' + guid + "/related"; + } else { + return categoryUrl + '/' + guid; + } + } else { + return categoryUrl; + } + }, + termApiUrl: function(options) { + var guid = options && options.guid, + list = options && options.list, + related = options && options.related, + termUrl = this.glossaryApiUrl() + '/' + (list ? 'terms' : 'term'); + if (guid) { + if (related) { + return termUrl + '/' + guid + "/related"; + } else { + return termUrl + '/' + guid; + } + } else { + return termUrl; + } + }, + termToEntityApiUrl: function(guid) { + var termUrl = this.termApiUrl({ list: true }); + if (guid) { + return termUrl + '/' + guid + '/assignedEntities'; + } + }, versionApiUrl: function() { return this.baseUrl + '/admin/version'; }, diff --git a/dashboardv2/public/js/utils/Utils.js b/dashboardv2/public/js/utils/Utils.js index 5024b8f..ff6b5ff 100644 --- a/dashboardv2/public/js/utils/Utils.js +++ b/dashboardv2/public/js/utils/Utils.js @@ -283,6 +283,8 @@ define(['require', 'utils/Globals', 'pnotify', 'utils/Messages', 'utils/Enums', urlUpdate['tagUrl'] = options.url; } else if (Utils.getUrlState.isSearchTab(options.url)) { urlUpdate['searchUrl'] = options.url; + } else if (Utils.getUrlState.isGlossaryTab(options.url)) { + urlUpdate['glossaryUrl'] = options.url; } $.extend(Globals.saveApplicationState.tabState, urlUpdate); } @@ -313,6 +315,9 @@ define(['require', 'utils/Globals', 'pnotify', 'utils/Messages', 'utils/Enums', isSearchTab: function(url) { return this.getQueryUrl(url).firstValue == "search"; }, + isGlossaryTab: function(url) { + return this.getQueryUrl(url).firstValue == "glossary"; + }, isDetailPage: function(url) { return this.getQueryUrl(url).firstValue == "detailPage"; }, diff --git a/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js b/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js index 99e9602..5d5ad2b 100644 --- a/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js +++ b/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js @@ -50,6 +50,7 @@ define(['require', /** ui selector cache */ ui: { tagClick: '[data-id="tagClick"]', + termClick: '[data-id="termClick"]', propagatedTagDiv: '[data-id="propagatedTagDiv"]', title: '[data-id="title"]', editButton: '[data-id="editButton"]', @@ -57,8 +58,11 @@ define(['require', description: '[data-id="description"]', editBox: '[data-id="editBox"]', deleteTag: '[data-id="deleteTag"]', + deleteTerm: '[data-id="deleteTerm"]', addTag: '[data-id="addTag"]', + addTerm: '[data-id="addTerm"]', tagList: '[data-id="tagList"]', + termList: '[data-id="termList"]', propagatedTagList: '[data-id="propagatedTagList"]', fullscreenPanel: "#fullscreen_panel", tablist: '[data-id="tab-list"] li' @@ -81,7 +85,19 @@ define(['require', }); } }; + events["click " + this.ui.termClick] = function(e) { + if (e.target.nodeName.toLocaleLowerCase() != "i") { + Utils.setUrl({ + url: '#!/glossary/' + $(e.currentTarget).find('i').data('guid'), + mergeBrowserUrl: false, + urlParams: { gType: "term" }, + trigger: true + }); + } + }; + events["click " + this.ui.addTerm] = 'onClickAddTermBtn'; events["click " + this.ui.deleteTag] = 'onClickTagCross'; + events["click " + this.ui.deleteTerm] = 'onClickTermCross'; events["click " + this.ui.addTag] = 'onClickAddTagBtn'; events["click " + this.ui.tablist] = function(e) { var tabValue = $(e.currentTarget).attr('role'); @@ -101,7 +117,7 @@ define(['require', * @constructs */ initialize: function(options) { - _.extend(this, _.pick(options, 'value', 'collection', 'id', 'entityDefCollection', 'typeHeaders', 'enumDefCollection', 'classificationDefCollection')); + _.extend(this, _.pick(options, 'value', 'collection', 'id', 'entityDefCollection', 'typeHeaders', 'enumDefCollection', 'classificationDefCollection', 'glossaryCollection')); this.bindEvents(); $('body').addClass("detail-page"); }, @@ -156,6 +172,9 @@ define(['require', } else { this.generateTag([]); } + if (collectionJSON.relationshipAttributes && collectionJSON.relationshipAttributes.meanings) { + this.generateTerm(collectionJSON.relationshipAttributes.meanings); + } if (Globals.entityTypeConfList && _.isEmptyArray(Globals.entityTypeConfList)) { this.ui.editButtonContainer.html(ButtonsTmpl({ btn_edit: true })); } else { @@ -326,6 +345,30 @@ define(['require', } })); }, + onClickTermCross: function(e) { + var $el = $(e.currentTarget), + termGuid = $el.data('guid'), + termName = $el.text(), + that = this, + termObj = _.find(this.collection.first().get('entity').relationshipAttributes.meanings, { guid: termGuid }); + CommonViewFunction.removeCategoryTermAssociation({ + termGuid: termGuid, + model: { + guid: that.id, + relationshipGuid: termObj.relationshipGuid + }, + collection: that.glossaryCollection, + msg: "<div class='ellipsis'>Remove: " + "<b>" + _.escape(termName) + "</b> assignment from" + " " + "<b>" + this.name + "?</b></div>", + titleMessage: Messages.glossary.removeTermfromEntity, + isEntityView: true, + buttonText: "Remove", + showLoader: that.showLoader.bind(that), + hideLoader: that.hideLoader.bind(that), + callback: function() { + that.fetchCollection(); + } + }); + }, generateTag: function(tagObject) { var that = this, tagData = "", @@ -338,7 +381,7 @@ define(['require', val.entityGuid === that.id ? tag['self'].push(val) : tag['propagated'].push(val); }); _.each(tag.self, function(val) { - tagData += '<span class="btn btn-action btn-sm btn-icon btn-blue" title=' + val.typeName + ' data-id="tagClick"><span>' + val.typeName + '</span><i class="fa fa-close" data-id="deleteTag" data-type="tag" title="Delete Tag"></i></span>'; + tagData += '<span class="btn btn-action btn-sm btn-icon btn-blue" title=' + val.typeName + ' data-id="tagClick"><span>' + val.typeName + '</span><i class="fa fa-close" data-id="deleteTag" data-type="tag" title="Remove Tag"></i></span>'; }); _.each(tag.propagated, function(val) { propagatedTagListData += '<span class="btn btn-action btn-sm btn-icon btn-blue" title=' + val.typeName + ' data-id="tagClick"><span>' + val.typeName + '</span></span>'; @@ -350,6 +393,15 @@ define(['require', this.ui.propagatedTagList.html(propagatedTagListData); }, + generateTerm: function(data) { + var that = this, + termData = ""; + _.each(data, function(val) { + termData += '<span class="btn btn-action btn-sm btn-icon btn-blue" title=' + val.displayText + ' data-id="termClick"><span>' + val.displayText + '</span><i class="fa fa-close" data-id="deleteTerm" data-guid="' + val.guid + '" data-type="term" title="Remove Term"></i></span>'; + }); + this.ui.termList.find("span.btn").remove(); + this.ui.termList.prepend(termData); + }, hideLoader: function() { Utils.hideTitleLoader(this.$('.page-title .fontLoader'), this.$('.entityDetail')); }, @@ -381,6 +433,23 @@ define(['require', }); }); }, + onClickAddTermBtn: function(e) { + var that = this; + require(['views/glossary/AssignTermLayoutView'], function(AssignTermLayoutView) { + var view = new AssignTermLayoutView({ + guid: that.id, + callback: function() { + that.fetchCollection(); + }, + showLoader: that.showLoader.bind(that), + hideLoader: that.hideLoader.bind(that), + glossaryCollection: that.glossaryCollection + }); + view.modal.on('ok', function() { + Utils.showTitleLoader(that.$('.page-title .fontLoader'), that.$('.entityDetail')); + }); + }); + }, renderEntityDetailTableLayoutView: function(obj) { var that = this; require(['views/entity/EntityDetailTableLayoutView'], function(EntityDetailTableLayoutView) { diff --git a/dashboardv2/public/js/views/glossary/AssignTermLayoutView.js b/dashboardv2/public/js/views/glossary/AssignTermLayoutView.js new file mode 100644 index 0000000..fd7631d --- /dev/null +++ b/dashboardv2/public/js/views/glossary/AssignTermLayoutView.js @@ -0,0 +1,151 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + */ + +define(['require', + 'backbone', + 'hbs!tmpl/glossary/AssignTermLayoutViewTmpl', + 'utils/Utils', + 'utils/UrlLinks', + 'modules/Modal' +], function(require, Backbone, AssignTermLayoutViewTmpl, Utils, UrlLinks, Modal) { + + var AssignTermLayoutView = Backbone.Marionette.LayoutView.extend( + /** @lends AssignTermLayoutView */ + { + _viewName: 'AssignTermLayoutView', + + template: AssignTermLayoutViewTmpl, + + templateHelpers: function() { + return {}; + }, + + /** Layout sub regions */ + regions: { + RGlossaryTree: "#r_glossaryTree" + }, + + /** ui selector cache */ + ui: {}, + /** ui events hash */ + events: function() { + var events = {}; + return events; + }, + /** + * intialize a new AssignTermLayoutView Layout + * @constructs + */ + initialize: function(options) { + _.extend(this, _.pick(options, 'glossaryCollection', 'guid', 'callback', 'hideLoader', 'isCategoryView', 'categoryData', 'isTermView', 'termData')); + var that = this; + this.options = options; + if (!this.isCategoryView && !this.isTermView) { + this.isEntityView = true; + } + this.glossary = { + selectedItem: {} + } + var title = ""; + if (this.isCategoryView || this.isEntityView) { + title = ("Assign term to " + (this.isCategoryView ? "Category" : "entity")) + } else { + title = "Assign Category to term"; + } + this.modal = new Modal({ + "title": title, + "content": this, + "cancelText": "Cancel", + "okCloses": false, + "okText": "Assign", + "allowCancel": true + }); + this.modal.open(); + this.modal.on('closeModal', function() { + that.modal.trigger('cancel'); + if (that.assignTermError && that.hideLoader) { + that.hideLoader(); + } + if (options.onModalClose) { + options.onModalClose() + } + }); + this.modal.on('ok', function() { + that.assignTerm(); + }); + }, + bindEvents: function() {}, + onRender: function() { + this.renderGlossaryTree(); + }, + assignTerm: function() { + this.assignTermError = false; + var that = this, + data = [], + selectedItem = this.glossary.selectedItem, + selectedGuid = selectedItem.guid, + ajaxOptions = { + success: function(rModel, response) { + Utils.notifySuccess({ + content: (that.isCategoryView ? "Term" : "Category") + " is associated successfully " + }); + that.modal.trigger('closeModal'); + if (that.callback) { + that.callback(); + } + }, + cust_error: function() { + that.assignTermError = true; + } + }, + model = new this.glossaryCollection.model(); + if (this.isCategoryView) { + data = _.extend({}, this.categoryData); + if (data.terms) { + data.terms.push({ "termGuid": selectedGuid }); + } else { + data.terms = [{ "termGuid": selectedGuid }]; + } + model.assignTermToCategory(_.extend(ajaxOptions, { data: JSON.stringify(data), guid: data.guid })); + } else if (this.isTermView) { + data = _.extend({}, this.termData); + if (data.categories) { + data.categories.push({ "categoryGuid": selectedGuid }); + } else { + data.categories = [{ "categoryGuid": selectedGuid }]; + } + model.assignCategoryToTerm(_.extend(ajaxOptions, { data: JSON.stringify(data), guid: data.guid })); + } else { + data.push({ "guid": that.guid }); + model.assignTermToEntity(selectedGuid, _.extend(ajaxOptions, { data: JSON.stringify(data) })); + } + }, + renderGlossaryTree: function() { + var that = this; + require(['views/glossary/GlossaryLayoutView'], function(GlossaryLayoutView) { + that.RGlossaryTree.show(new GlossaryLayoutView(_.extend({ + "isAssignTermView": that.isCategoryView, + "isAssignCategoryView": that.isTermView, + "isAssignEntityView": that.isEntityView, + "glossary": that.glossary + }, that.options))); + }); + }, + }); + return AssignTermLayoutView; +}); \ No newline at end of file diff --git a/dashboardv2/public/js/views/glossary/CreateEditCategoryTermLayoutView.js b/dashboardv2/public/js/views/glossary/CreateEditCategoryTermLayoutView.js new file mode 100644 index 0000000..c884229 --- /dev/null +++ b/dashboardv2/public/js/views/glossary/CreateEditCategoryTermLayoutView.js @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + */ + +define(['require', + 'backbone', + 'hbs!tmpl/glossary/CreateEditCategoryTermLayoutView_tmpl', + 'utils/Utils', + 'utils/UrlLinks' +], function(require, Backbone, CreateEditCategoryTermLayoutViewTmpl, Utils, UrlLinks) { + + var CreateEditCategoryTermLayoutView = Backbone.Marionette.LayoutView.extend( + /** @lends CreateEditCategoryTermLayoutView */ + { + _viewName: 'CreateEditCategoryTermLayoutView', + + template: CreateEditCategoryTermLayoutViewTmpl, + + templateHelpers: function() { + return { + create: this.create, + modelJSON: this.modelJSON + }; + }, + + /** Layout sub regions */ + regions: {}, + + /** ui selector cache */ + ui: { + "qualifiedName": "[data-id='qualifiedName']", + "displayName": "[data-id='displayName']", + "shortDescription": "[data-id='shortDescription']", + "longDescription": "[data-id='longDescription']", + "categoryTermForm": "[data-id='categoryTermForm']" + }, + /** ui events hash */ + events: function() { + var events = {}; + return events; + }, + /** + * intialize a new CreateEditCategoryTermLayoutView Layout + * @constructs + */ + initialize: function(options) { + _.extend(this, _.pick(options, 'glossaryCollection', 'modelJSON')); + if (!this.modelJSON) { + this.create = true; + } + }, + bindEvents: function() {}, + onRender: function() {} + }); + return CreateEditCategoryTermLayoutView; +}); \ No newline at end of file diff --git a/dashboardv2/public/js/views/glossary/CreateEditGlossaryLayoutView.js b/dashboardv2/public/js/views/glossary/CreateEditGlossaryLayoutView.js new file mode 100644 index 0000000..e06b05b --- /dev/null +++ b/dashboardv2/public/js/views/glossary/CreateEditGlossaryLayoutView.js @@ -0,0 +1,69 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + */ + +define(['require', + 'backbone', + 'hbs!tmpl/glossary/CreateEditGlossaryLayoutView_tmpl', + 'utils/Utils', + 'utils/UrlLinks' +], function(require, Backbone, CreateEditGlossaryLayoutViewTmpl, Utils, UrlLinks) { + + var CreateEditGlossaryLayoutView = Backbone.Marionette.LayoutView.extend( + /** @lends CreateEditGlossaryLayoutView */ + { + _viewName: 'CreateEditGlossaryLayoutView', + + template: CreateEditGlossaryLayoutViewTmpl, + + templateHelpers: function() { + return { + create: this.create + }; + }, + + /** Layout sub regions */ + regions: {}, + + /** ui selector cache */ + ui: { + "qualifiedName": "[data-id='qualifiedName']", + "displayName": "[data-id='displayName']", + "shortDescription": "[data-id='shortDescription']", + "longDescription": "[data-id='longDescription']", + "glossaryForm": "[data-id='glossaryForm']" + }, + /** ui events hash */ + events: function() { + var events = {}; + return events; + }, + /** + * intialize a new CreateEditGlossaryLayoutView Layout + * @constructs + */ + initialize: function(options) { + _.extend(this, _.pick(options, 'glossaryCollection', 'model')); + if (!this.model) { + this.create = true; + } + }, + bindEvents: function() {}, + onRender: function() {} + }); + return CreateEditGlossaryLayoutView; +}); \ No newline at end of file diff --git a/dashboardv2/public/js/views/glossary/GlossaryDetailLayoutView.js b/dashboardv2/public/js/views/glossary/GlossaryDetailLayoutView.js new file mode 100644 index 0000000..2ba3c45 --- /dev/null +++ b/dashboardv2/public/js/views/glossary/GlossaryDetailLayoutView.js @@ -0,0 +1,294 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + */ + +define(['require', + 'backbone', + 'hbs!tmpl/glossary/GlossaryDetailLayoutView_tmpl', + 'utils/Utils', + 'utils/Messages', + 'utils/Globals', + 'utils/CommonViewFunction' +], function(require, Backbone, GlossaryDetailLayoutViewTmpl, Utils, Messages, Globals, CommonViewFunction) { + 'use strict'; + + var GlossaryDetailLayoutView = Backbone.Marionette.LayoutView.extend( + /** @lends GlossaryDetailLayoutView */ + { + _viewName: 'GlossaryDetailLayoutView', + + template: GlossaryDetailLayoutViewTmpl, + + /** Layout sub regions */ + regions: { + RSearchResultLayoutView: "#r_searchResultLayoutView", + }, + templateHelpers: function() { + return { + isTermView: this.isTermView, + isCategoryView: this.isCategoryView + }; + }, + + /** ui selector cache */ + ui: { + details: "[data-id='details']", + editButton: "[data-id='editButton']", + title: "[data-id='title']", + shortDescription: "[data-id='shortDescription']", + longDescription: "[data-id='longDescription']", + categoryList: "[data-id='categoryList']", + removeCategory: "[data-id='removeCategory']", + categoryClick: "[data-id='categoryClick']", + addCategory: "[data-id='addCategory']", + termList: "[data-id='termList']", + removeTerm: "[data-id='removeTerm']", + termClick: "[data-id='termClick']", + addTerm: "[data-id='addTerm']" + }, + /** ui events hash */ + events: function() { + var events = {}; + events["click " + this.ui.categoryClick] = function(e) { + if (e.target.nodeName.toLocaleLowerCase() == "i") { + this.onClickRemoveAssociationBtn(e); + } else { + var guid = $(e.currentTarget).data('guid'), + categoryObj = _.find(this.data.categories, { "categoryGuid": guid }); + this.glossary.selectedItem = { "type": "GlossaryCategory", "guid": guid, "model": categoryObj }; + Utils.setUrl({ + url: '#!/glossary/' + guid, + mergeBrowserUrl: false, + urlParams: { gType: "category" }, + trigger: true, + updateTabState: true + }); + } + }; + events["click " + this.ui.termClick] = function(e) { + if (e.target.nodeName.toLocaleLowerCase() == "i") { + this.onClickRemoveAssociationBtn(e); + } else { + var guid = $(e.currentTarget).data('guid'), + termObj = _.find(this.data.terms, { "termGuid": guid }); + this.glossary.selectedItem = { "type": "GlossaryTerm", "guid": guid, "model": termObj }; + Utils.setUrl({ + url: '#!/glossary/' + guid, + mergeBrowserUrl: false, + urlParams: { gType: "term" }, + trigger: true, + updateTabState: true + }); + } + }; + events["click " + this.ui.editButton] = function(e) { + var that = this, + model = this.glossaryCollection.fullCollection.get(this.guid); + if (this.isGlossaryView) { + CommonViewFunction.createEditGlossaryCategoryTerm({ + "model": model, + "isGlossaryView": this.isGlossaryView, + "collection": this.glossaryCollection, + "callback": function(newModel) { + that.data = newModel; + model.set(newModel); + that.renderDetails(that.data); + //that.glossaryCollection.trigger("update:details"); + } + }); + } else { + CommonViewFunction.createEditGlossaryCategoryTerm({ + "isTermView": this.isTermView, + "isCategoryView": this.isCategoryView, + "model": this.data, + "collection": this.glossaryCollection, + "callback": function() { + that.getData(); + that.glossaryCollection.trigger("update:details"); + } + }); + } + }; + events["click " + this.ui.addTerm] = 'onClickAddTermBtn'; + events["click " + this.ui.addCategory] = 'onClickAddCategoryBtn'; + return events; + }, + /** + * intialize a new GlossaryDetailLayoutView Layout + * @constructs + */ + initialize: function(options) { + _.extend(this, _.pick(options, 'guid', 'glossaryCollection', 'glossary', 'collection', 'typeHeaders', 'value', 'entityDefCollection', 'enumDefCollection', 'classificationDefCollection')); + if (this.value && this.value.gType) { + if (this.value.gType == "category") { + this.isCategoryView = true; + } else if (this.value.gType == "term") { + this.isTermView = true; + } else { + this.isGlossaryView = true; + } + } + }, + onRender: function() { + this.getData(); + this.bindEvents(); + }, + bindEvents: function() { + var that = this; + }, + getData: function() { + if (this.glossaryCollection.fullCollection.length && this.isGlossaryView) { + this.data = this.glossaryCollection.fullCollection.get(this.guid).toJSON(); + this.renderDetails(this.data); + } else { + Utils.showTitleLoader(this.$('.page-title .fontLoader'), this.ui.details); + var getApiFunctionKey = "getCategory", + that = this; + if (this.isTermView) { + getApiFunctionKey = "getTerm"; + } + this.glossaryCollection[getApiFunctionKey]({ + "guid": this.guid, + "ajaxOptions": { + success: function(data) { + if (that.isTermView) { + that.renderSearchResultLayoutView(); + } + that.data = data; + that.glossary.selectedItem.model = data; + that.glossary.selectedItem.guid = data.guid; + that.renderDetails(data) + }, + cust_error: function() {} + } + }); + } + }, + renderDetails: function(data) { + Utils.hideTitleLoader(this.$('.fontLoader'), this.ui.details); + this.ui.title.text(data.displayName || data.displayText || data.qualifiedName); + this.ui.shortDescription.text(data.shortDescription); + this.ui.longDescription.text(data.longDescription); + this.generateCategories(data.categories); + this.generateTerm(data.terms); + + }, + generateCategories: function(data) { + var that = this, + categories = ""; + _.each(data, function(val) { + var name = _.escape(val.displayText); + categories += '<span data-guid="' + val.categoryGuid + '"" class="btn btn-action btn-sm btn-icon btn-blue" title=' + _.escape(name) + ' data-id="categoryClick"><span>' + name + '</span><i class="fa fa-close" data-id="removeCategory" data-type="category" title="Remove Category"></i></span>'; + }); + this.ui.categoryList.find("span.btn").remove(); + this.ui.categoryList.prepend(categories); + }, + generateTerm: function(data) { + var that = this, + terms = ""; + _.each(data, function(val) { + var name = _.escape(val.displayText); + terms += '<span data-guid="' + val.termGuid + '"" class="btn btn-action btn-sm btn-icon btn-blue" title=' + _.escape(name) + ' data-id="termClick"><span>' + name + '</span><i class="fa fa-close" data-id="removeTerm" data-type="term" title="Remove Term"></i></span>'; + }); + this.ui.termList.find("span.btn").remove(); + this.ui.termList.prepend(terms); + + }, + onClickAddTermBtn: function(e) { + var that = this; + require(['views/glossary/AssignTermLayoutView'], function(AssignTermLayoutView) { + var view = new AssignTermLayoutView({ + categoryData: that.data, + isCategoryView: that.isCategoryView, + callback: function() { + that.getData(); + }, + glossaryCollection: that.glossaryCollection + }); + view.modal.on('ok', function() { + that.hideLoader(); + }); + }); + }, + onClickAddCategoryBtn: function(e) { + var that = this; + require(['views/glossary/AssignTermLayoutView'], function(AssignTermLayoutView) { + var view = new AssignTermLayoutView({ + termData: that.data, + isTermView: that.isTermView, + callback: function() { + that.getData(); + }, + glossaryCollection: that.glossaryCollection + }); + view.modal.on('ok', function() { + that.hideLoader(); + }); + }); + }, + onClickRemoveAssociationBtn: function(e) { + var $el = $(e.currentTarget), + guid = $el.data('guid'), + name = $el.text(), + that = this; + CommonViewFunction.removeCategoryTermAssociation({ + selectedGuid: guid, + model: that.data, + collection: that.glossaryCollection, + msg: "<div class='ellipsis'>Remove: " + "<b>" + _.escape(name) + "</b> assignment from" + " " + "<b>" + that.data.displayName + "?</b></div>", + titleMessage: Messages.glossary[that.isTermView ? "removeCategoryfromTerm" : "removeTermfromCategory"], + isCategoryView: that.isCategoryView, + isTermView: that.isTermView, + buttonText: "Remove", + showLoader: that.hideLoader.bind(that), + hideLoader: that.hideLoader.bind(that), + callback: function() { + that.getData(); + } + }); + }, + showLoader: function() { + Utils.showTitleLoader(this.$('.page-title .fontLoader'), this.ui.details); + }, + hideLoader: function() { + Utils.hideTitleLoader(this.$('.page-title .fontLoader'), this.ui.details); + }, + renderSearchResultLayoutView: function() { + var that = this; + require(['views/search/SearchResultLayoutView'], function(SearchResultLayoutView) { + var value = { + 'tag': "PII", + 'searchType': 'basic' + }; + if (that.RSearchResultLayoutView) { + that.RSearchResultLayoutView.show(new SearchResultLayoutView({ + "value": _.extend({}, that.value, { "searchType": "basic" }), + "termName": that.data.qualifiedName, + "guid": that.guid, + "entityDefCollection": that.entityDefCollection, + "typeHeaders": that.typeHeaders, + "tagCollection": that.collection, + "enumDefCollection": that.enumDefCollection, + "classificationDefCollection": that.classificationDefCollection, + "fromView": "glossary" + })); + } + }); + }, + }); + return GlossaryDetailLayoutView; +}); \ No newline at end of file diff --git a/dashboardv2/public/js/views/glossary/GlossaryLayoutView.js b/dashboardv2/public/js/views/glossary/GlossaryLayoutView.js new file mode 100644 index 0000000..57c58c7 --- /dev/null +++ b/dashboardv2/public/js/views/glossary/GlossaryLayoutView.js @@ -0,0 +1,528 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + */ + +define(['require', + 'backbone', + 'hbs!tmpl/glossary/GlossaryLayoutView_tmpl', + 'utils/Utils', + 'utils/Messages', + 'utils/Globals', + 'utils/CommonViewFunction', + 'jstree' +], function(require, Backbone, GlossaryLayoutViewTmpl, Utils, Messages, Globals, CommonViewFunction) { + 'use strict'; + + var GlossaryLayoutView = Backbone.Marionette.LayoutView.extend( + /** @lends GlossaryLayoutView */ + { + _viewName: 'GlossaryLayoutView', + + template: GlossaryLayoutViewTmpl, + + /** Layout sub regions */ + regions: {}, + templateHelpers: function() { + return { + isAssignView: this.isAssignView + }; + }, + + /** ui selector cache */ + ui: { + createGlossary: "[data-id='createGlossary']", + refreshGlossary: "[data-id='refreshGlossary']", + searchTerm: "[data-id='searchTerm']", + searchCategory: "[data-id='searchCategory']", + glossaryView: 'input[name="glossaryView"]', + termTree: "[data-id='termTree']", + categoryTree: "[data-id='categoryTree']" + }, + /** ui events hash */ + events: function() { + var events = {}; + events["change " + this.ui.glossaryView] = 'glossaryViewToggle'; + events["click " + this.ui.createGlossary] = function(e) { + var that = this; + if (e) { + $(e.currentTarget).attr("disabled", "true"); + } + CommonViewFunction.createEditGlossaryCategoryTerm({ + isGlossaryView: true, + collection: this.glossaryCollection, + callback: function() { + that.ui.createGlossary.removeAttr("disabled"); + that.getGlossary(); + }, + onModalClose: function() { + that.ui.createGlossary.removeAttr("disabled"); + } + }) + }; + events["click " + this.ui.refreshGlossary] = 'getGlossary'; + events["keyup " + this.ui.searchTerm] = function() { + this.ui.termTree.jstree("search", this.ui.searchTerm.val()); + }; + events["keyup " + this.ui.searchCategory] = function() { + this.ui.categoryTree.jstree("search", this.ui.searchCategory.val()); + }; + return events; + }, + /** + * intialize a new GlossaryLayoutView Layout + * @constructs + */ + initialize: function(options) { + _.extend(this, _.pick(options, 'guid', 'value', 'glossaryCollection', 'glossary', 'isAssignTermView', 'isAssignCategoryView', 'isAssignEntityView')); + this.viewType = "term"; + this.isAssignView = this.isAssignTermView || this.isAssignCategoryView || this.isAssignEntityView; + this.bindEvents(); + this.query = { + term: { + url: null + }, + category: { + url: null + } + }; + }, + bindEvents: function() { + var that = this; + this.listenTo(this.glossaryCollection.fullCollection, "reset add remove change", function() { + this.generateTree(); + }, this); + this.listenTo(this.glossaryCollection, "update:details", function() { + this.getGlossary(); + }, this); + $('body').on('click', '.termPopoverOptions li, .categoryPopoverOptions li', function(e) { + that.$('.termPopover,.categoryPopover').popover('hide'); + that[$(this).find('a').data('fn')](e) + }); + }, + onRender: function() { + if (this.isAssignCategoryView) { + this.$('.category-view').show(); + this.$('.term-view').hide(); + } + if (this.isAssignView && this.glossaryCollection.fullCollection.length) { + this.generateTree(); + } else { + this.getGlossary(); + } + }, + glossaryViewToggle: function(e) { + if (e.currentTarget.checked) { + this.$('.category-view').show(); + this.$('.term-view').hide(); + this.viewType = "category"; + } else { + this.$('.term-view').show(); + this.$('.category-view').hide(); + this.viewType = "term"; + } + if (Utils.getUrlState.isGlossaryTab()) { + // var name = this.query[this.viewType].url; + // var guid = this.glossary.selectedItem.guid; + // Utils.setUrl({ + // url: '#!/glossary/' + guid, + // urlParams: { + // viewType: this.viewType, + // }, + // mergeBrowserUrl: false, + // trigger: true, + // updateTabState: true + // }); + } + }, + getGlossary: function() { + this.glossaryCollection.fetch({ reset: true }); + }, + generateCategoryData: function(options) { + return _.map(options.data, function(obj) { + return { + "text": obj.displayText, + "icon": "fa fa-files-o", + "guid": obj.categoryGuid, + "id": obj.categoryGuid, + "glossaryId": options.node.glossaryId, + "glossaryName": options.node.glossaryName, + "model": obj, + "type": "GlossaryCategory", + "children": true + } + }); + }, + getCategory: function(options) { + var that = this; + this.glossaryCollection.getCategory({ + "guid": options.node.guid, + "related": true, + "ajaxOptions": { + success: function(data) { + if (data && data.children) { + options.callback(that.generateCategoryData(_.extend({}, { "data": data.children }, options))); + } else { + options.callback([]); + } + }, + cust_error: function() { + options.callback([]); + } + } + }); + }, + generateData: function(opt) { + var that = this, + type = opt.type; + var getSelectedState = function(options) { + var objGuid = options.objGuid, + node = options.node, + index = options.index; + if (!that.guid) { + var selectedItem = { + "type": "Glossary", + "model": that.glossaryCollection.first().toJSON() + }; + selectedItem.text = selectedItem.model.displayName; + selectedItem.guid = selectedItem.model.guid; + if (index == 0 && selectedItem.guid == objGuid) { + that.glossary.selectedItem = selectedItem; + return { + 'opened': true, + 'selected': true + } + } + } else { + if (that.guid == objGuid) { + that.glossary.selectedItem = node + return { + 'opened': true, + 'selected': true + } + } + } + } + return this.glossaryCollection.fullCollection.map(function(model, i) { + var obj = model.toJSON(), + parent = { + "text": obj.displayName, + "icon": "fa fa-folder-o", + "guid": obj.guid, + "id": obj.guid, + "model": obj, + "type": obj.typeName ? obj.typeName : "Glossary", + "children": [] + } + parent.state = getSelectedState({ + index: i, + node: parent, + objGuid: obj.guid + }); + + if (type == "category" && obj.categories) { + _.each(obj.categories, function(category) { + if (category.parentCategoryGuid) { + return; + } + var type = category.typeName || "GlossaryCategory", + guid = category.categoryGuid, + categoryObj = { + "text": category.displayText, + "type": type, + "guid": guid, + "id": guid, + "parent": obj, + "glossaryId": obj.guid, + "glossaryName": obj.displayName, + "model": category, + "children": true, + "icon": "fa fa-files-o", + }; + categoryObj.state = getSelectedState({ + index: i, + node: categoryObj, + objGuid: guid + }) + parent.children.push(categoryObj) + }); + } + if (type == "term" && obj.terms) { + _.each(obj.terms, function(term) { + var type = term.typeName || "GlossaryTerm", + guid = term.termGuid, + termObj = { + "text": term.displayText, + "type": type, + "guid": guid, + "id": guid, + "parent": obj, + "glossaryName": obj.displayName, + "glossaryId": obj.guid, + "model": term, + "icon": "fa fa-file-o" + } + termObj.state = getSelectedState({ + index: i, + node: termObj, + objGuid: guid + }) + parent.children.push(termObj); + }); + } + return parent; + }); + }, + manualRender: function(options) { + _.extend(this, options); + this.triggerUrl(); + }, + generateTree: function() { + var $termTree = this.ui.termTree, + $categoryTree = this.ui.categoryTree, + that = this, + getTreeConfig = function(options) { + return { + "plugins": ["search", "themes", "core", "wholerow", "sort", "conditionalselect"], + "conditionalselect": function(node) { + if (that.isAssignView) { + return node.original.type != "Glossary" ? true : false; + } else { + return node.original.type != "NoAction" ? true : false; + } + }, + "core": { + "data": function(node, cb) { + if (node.id === "#") { + cb(that.generateData(options)); + } else { + that.getCategory({ "node": node.original, "callback": cb }); + } + }, + "themes": { + "name": that.isAssignView ? "default" : "default-dark", + "dots": true + }, + } + } + }, + treeLoaded = function() { + if (that.selectFirstNodeManually) { + that.selectFirstNodeManually = false; + var id = that.glossary.selectedItem.guid; + $treeEl.jstree('select_node', '#' + id + '_anchor'); + $treeEl.jstree('open_node', '#' + id + '_anchor'); + } + }, + createAction = function(options) { + var $el = options.el, + type = options.type, + popoverClassName = type == "term" ? "termPopover" : "categoryPopover"; + if (!that.isAssignView) { + var wholerowEl = $el.find("li[role='treeitem'] > .jstree-wholerow:not(:has(>div.tools))") + wholerowEl.append('<div class="tools"><i class="fa fa-ellipsis-h ' + popoverClassName + '"></i></div>'); + + if (type == "term") { + that.createTermAction(); + } else if (type == "category") { + that.createCategoryAction(); + } + } + }, + initializeTree = function(options) { + var $el = options.el, + type = options.type; + + $el.jstree(getTreeConfig({ + type: type + })).on("load_node.jstree", function(e, data) { + createAction(_.extend({}, options, data)); + }).on("open_node.jstree", function(e, data) { + createAction(_.extend({}, options, data)); + }) + .on("select_node.jstree", function(e, data) { + that.glossary.selectedItem = data.node.original; + //$("." + popoverClassName).popover('hide'); + that.triggerUrl(); + }).bind('loaded.jstree', function(e, data) { + treeLoaded(); + }); + }, + initializeTermTree = function() { + if ($termTree.data('jstree')) { + $('.termPopover').popover('destroy'); + $termTree.jstree(true).refresh(); + } else { + initializeTree({ + el: $termTree, + type: "term" + }); + } + }, + initializeCategoryTree = function() { + if ($categoryTree.data('jstree')) { + $categoryTree.jstree(true).refresh(); + } else { + initializeTree({ + el: $categoryTree, + type: "category" + }) + } + } + if (this.isAssignView) { + if (this.isAssignTermView || this.isAssignEntityView) { + initializeTermTree(); + } else if (this.isAssignCategoryView) { + initializeCategoryTree(); + } + } else { + initializeTermTree(); + initializeCategoryTree(); + } + + + if (Utils.getUrlState.isGlossaryTab()) { + this.triggerUrl(); + } + this.glossaryCollection.trigger("render:done"); + }, + createTermAction: function() { + var that = this; + Utils.generatePopover({ + el: this.$('.termPopover'), + contentClass: 'termPopoverOptions', + popoverOptions: { + content: function() { + var node = that.glossary.selectedItem, + liString = ""; + if (node.type == "Glossary") { + liString = "<li data-type=" + node.type + " class='listTerm'><i class='fa fa-plus'></i> <a href='javascript:void(0)' data-fn='createSubNode'>Create Sub-Term</a></li>" + + "<li data-type=" + node.type + " class='listTerm'><i class='fa fa-trash-o'></i><a href='javascript:void(0)' data-fn='deleteNode'>Delete Glossary</a></li>" + } else { + liString = "<li data-type=" + node.type + " class='listTerm'><i class='fa fa-trash-o'></i><a href='javascript:void(0)' data-fn='deleteNode'>Delete Term</a></li>" + } + return "<ul>" + liString + "</ul>"; + } + } + }); + }, + createCategoryAction: function() { + var that = this; + Utils.generatePopover({ + el: this.$('.categoryPopover'), + contentClass: 'categoryPopoverOptions', + popoverOptions: { + content: function() { + var node = that.glossary.selectedItem, + liString = ""; + if (node.type == "Glossary") { + liString = "<li data-type=" + node.type + " class='listTerm'><i class='fa fa-plus'></i> <a href='javascript:void(0)' data-fn='createSubNode'>Create Sub-Category</a></li>" + + "<li data-type=" + node.type + " class='listTerm'><i class='fa fa-trash-o'></i><a href='javascript:void(0)' data-fn='deleteNode'>Delete Glossary</a></li>" + } else { + liString = "<li data-type=" + node.type + " class='listTerm'><i class='fa fa-plus'></i> <a href='javascript:void(0)' data-fn='createSubNode'>Create Sub-Category</a></li>" + + "<li data-type=" + node.type + " class='listTerm'><i class='fa fa-trash-o'></i><a href='javascript:void(0)' data-fn='deleteNode'>Delete Category</a></li>" + } + return "<ul>" + liString + "</ul>"; + } + } + }); + }, + createSubNode: function(opt) { + var that = this, + type = this.glossary.selectedItem.type; + if ((type == "Glossary" || type == "GlossaryCategory") && this.viewType == "category") { + CommonViewFunction.createEditGlossaryCategoryTerm({ + "isCategoryView": true, + "collection": that.glossaryCollection, + "callback": function() { + that.getGlossary(); + }, + "node": this.glossary.selectedItem + }) + } else { + CommonViewFunction.createEditGlossaryCategoryTerm({ + "isTermView": true, + "callback": function() { + that.getGlossary(); + }, + "collection": that.glossaryCollection, + "node": this.glossary.selectedItem + }) + } + }, + deleteNode: function(opt) { + var that = this, + messageType = "", + options = { + success: function(rModel, response) { + Utils.notifySuccess({ + content: messageType + Messages.deleteSuccessMessage + }); + that.getGlossary(); + } + }, + type = this.glossary.selectedItem.type, + guid = this.glossary.selectedItem.guid + if (type == "Glossary") { + messageType = "Glossary"; + this.glossaryCollection.fullCollection.get(guid).destroy(options); + } else if (type == "GlossaryCategory") { + messageType = "Category" + new this.glossaryCollection.model().deleteCategory(guid, options); + } else if (type == "GlossaryTerm") { + messageType = "Term"; + new this.glossaryCollection.model().deleteTerm(guid, options); + } + }, + triggerUrl: function() { + if (this.isAssignView) { + return; + } + var selectedItem = this.glossary.selectedItem; + if (this.glossaryCollection.length && _.isEmpty(selectedItem)) { + selectedItem = { "model": this.glossaryCollection.first().toJSON() }; + selectedItem.guid = selectedItem.model.guid; + selectedItem.type = "glossary"; + this.glossary.selectedItem = selectedItem; + this.selectFirstNodeManually = true; + } + if (_.isEmpty(selectedItem)) { + return; + } + var type = selectedItem.type; + if (Utils.getUrlState.isGlossaryTab() || Utils.getUrlState.isDetailPage()) { + var urlParams = { gType: "glossary" }, + guid = selectedItem.guid; + if (type === "GlossaryTerm") { + urlParams.gType = "term"; + } else if (type === "GlossaryCategory") { + urlParams.gType = "category"; + } + if (selectedItem.glossaryId) { + urlParams["gId"] = selectedItem.glossaryId; + } + Utils.setUrl({ + url: '#!/glossary/' + guid, + mergeBrowserUrl: false, + trigger: true, + urlParams: urlParams, + updateTabState: true + }); + } + } + }); + return GlossaryLayoutView; +}); \ No newline at end of file diff --git a/dashboardv2/public/js/views/schema/SchemaLayoutView.js b/dashboardv2/public/js/views/schema/SchemaLayoutView.js index 41ec8eb..4bee673 100644 --- a/dashboardv2/public/js/views/schema/SchemaLayoutView.js +++ b/dashboardv2/public/js/views/schema/SchemaLayoutView.js @@ -174,12 +174,12 @@ define(['require', that.$('.multiSelectTag').hide(); Utils.generatePopover({ el: that.$('[data-id="showMoreLess"]'), - contentClass: 'popover-tag', + contentClass: 'popover-tag-term', viewFixedPopover: true, popoverOptions: { container: null, content: function() { - return $(this).find('.popup-tag').children().clone(); + return $(this).find('.popup-tag-term').children().clone(); } } }); diff --git a/dashboardv2/public/js/views/search/SearchResultLayoutView.js b/dashboardv2/public/js/views/search/SearchResultLayoutView.js index 6835f03..0318c32 100644 --- a/dashboardv2/public/js/views/search/SearchResultLayoutView.js +++ b/dashboardv2/public/js/views/search/SearchResultLayoutView.js @@ -51,7 +51,9 @@ define(['require', /** ui selector cache */ ui: { tagClick: '[data-id="tagClick"]', + termClick: '[data-id="termClick"]', addTag: '[data-id="addTag"]', + addTerm: '[data-id="addTerm"]', paginationDiv: '[data-id="paginationDiv"]', previousData: "[data-id='previousData']", nextData: "[data-id='nextData']", @@ -94,6 +96,20 @@ define(['require', }); } }; + events["click " + this.ui.termClick] = function(e) { + var scope = $(e.currentTarget); + if (e.target.nodeName.toLocaleLowerCase() == "i") { + this.onClickTermCross(e); + } else { + this.triggerUrl({ + url: '#!/glossary/' + scope.find('i').data('termguid'), + urlParams: { gType: "term" }, + mergeBrowserUrl: false, + trigger: true, + updateTabState: null + }); + } + }; events["keyup " + this.ui.gotoPage] = function(e) { var code = e.which, goToPage = parseInt(e.currentTarget.value); @@ -111,6 +127,7 @@ define(['require', events["change " + this.ui.showPage] = 'changePageLimit'; events["click " + this.ui.gotoPagebtn] = 'gotoPagebtn'; events["click " + this.ui.addTag] = 'checkedValue'; + events["click " + this.ui.addTerm] = 'onClickAddTermBtn'; events["click " + this.ui.addAssignTag] = 'checkedValue'; events["click " + this.ui.nextData] = "onClicknextData"; events["click " + this.ui.previousData] = "onClickpreviousData"; @@ -125,7 +142,7 @@ define(['require', * @constructs */ initialize: function(options) { - _.extend(this, _.pick(options, 'value', 'initialView', 'isTypeTagNotExists', 'classificationDefCollection', 'entityDefCollection', 'typeHeaders', 'searchVent', 'enumDefCollection', 'tagCollection', 'searchTableColumns', 'isDisable', 'fromView')); + _.extend(this, _.pick(options, 'value', 'guid', 'initialView', 'isTypeTagNotExists', 'classificationDefCollection', 'entityDefCollection', 'typeHeaders', 'searchVent', 'enumDefCollection', 'tagCollection', 'searchTableColumns', 'isDisable', 'fromView', 'glossaryCollection', 'termName')); this.entityModel = new VEntity(); this.searchCollection = new VSearchList(); this.limit = 25; @@ -200,7 +217,7 @@ define(['require', this.updateColumnList(state); var excludeDefaultColumn = []; if (this.value && this.value.type) { - excludeDefaultColumn = _.without(this.searchTableColumns[this.value.type], "selected", "name", "description", "typeName", "owner", "tag"); + excludeDefaultColumn = _.without(this.searchTableColumns[this.value.type], "selected", "name", "description", "typeName", "owner", "tag", "term"); if (this.searchTableColumns[this.value.type] === null) { this.ui.columnEmptyInfo.show(); } else { @@ -459,7 +476,7 @@ define(['require', if (value.searchType) { this.searchCollection.url = UrlLinks.searchApiUrl(value.searchType); } - _.extend(this.searchCollection.queryParams, { 'limit': this.limit, 'offset': this.offset, 'query': _.trim(value.query), 'typeName': value.type || null, 'classification': value.tag || null }); + _.extend(this.searchCollection.queryParams, { 'limit': this.limit, 'offset': this.offset, 'query': _.trim(value.query), 'typeName': value.type || null, 'classification': value.tag || null, 'termName': that.termName || null }); if (value.profileDBView && value.typeName && value.guid) { var profileParam = {}; profileParam['guid'] = value.guid; @@ -470,7 +487,7 @@ define(['require', } if (isPostMethod) { this.searchCollection.filterObj = _.extend({}, filterObj); - apiObj['data'] = _.extend(checkBoxValue, filterObj, _.pick(this.searchCollection.queryParams, 'query', 'excludeDeletedEntities', 'limit', 'offset', 'typeName', 'classification')) + apiObj['data'] = _.extend(checkBoxValue, filterObj, _.pick(this.searchCollection.queryParams, 'query', 'excludeDeletedEntities', 'limit', 'offset', 'typeName', 'classification', 'termName')) Globals.searchApiCallRef = this.searchCollection.getBasicRearchResult(apiObj); } else { apiObj.data = null; @@ -482,7 +499,7 @@ define(['require', } } else { if (isPostMethod) { - apiObj['data'] = _.extend(checkBoxValue, filterObj, _.pick(this.searchCollection.queryParams, 'query', 'excludeDeletedEntities', 'limit', 'offset', 'typeName', 'classification')); + apiObj['data'] = _.extend(checkBoxValue, filterObj, _.pick(this.searchCollection.queryParams, 'query', 'excludeDeletedEntities', 'limit', 'offset', 'typeName', 'classification', 'termName')); Globals.searchApiCallRef = this.searchCollection.getBasicRearchResult(apiObj); } else { apiObj.data = null; @@ -561,12 +578,12 @@ define(['require', this.hideLoader(); Utils.generatePopover({ el: this.$('[data-id="showMoreLess"]'), - contentClass: 'popover-tag', + contentClass: 'popover-tag-term', viewFixedPopover: true, popoverOptions: { container: null, content: function() { - return $(this).find('.popup-tag').children().clone(); + return $(this).find('.popup-tag-term').children().clone(); } } }); @@ -580,6 +597,7 @@ define(['require', if (this.value && this.value.searchType === "basic" && this.searchTableColumns && (this.searchTableColumns[this.value.type] !== undefined)) { columnToShow = this.searchTableColumns[this.value.type] == null ? [] : this.searchTableColumns[this.value.type]; } + col['Check'] = { name: "selected", label: "Select", @@ -590,6 +608,7 @@ define(['require', headerCell: "select-all" }; + col['name'] = { label: this.value && this.value.profileDBView ? "Table Name" : "Name", cell: "html", @@ -617,6 +636,7 @@ define(['require', } }) }; + col['owner'] = { label: "Owner", cell: "String", @@ -634,6 +654,8 @@ define(['require', } }) }; + + if (this.value && this.value.profileDBView) { col['createTime'] = { label: "Date Created", @@ -653,6 +675,7 @@ define(['require', } } if (this.value && !this.value.profileDBView) { + col['description'] = { label: "Description", cell: "String", @@ -670,6 +693,8 @@ define(['require', } }) }; + + col['typeName'] = { label: "Type", cell: "Html", @@ -688,6 +713,9 @@ define(['require', }) }; this.getTagCol({ 'col': col, 'columnToShow': columnToShow }); + if (this.fromView != "glossary") { + this.getTermCol({ 'col': col, 'columnToShow': columnToShow }); + } if (this.value && this.value.searchType === "basic") { var def = this.entityDefCollection.fullCollection.find({ name: this.value.type }); @@ -806,6 +834,34 @@ define(['require', }; } }, + getTermCol: function(options) { + var that = this, + columnToShow = options.columnToShow, + col = options.col; + if (col) { + col['term'] = { + label: "Term", + cell: "Html", + editable: false, + sortable: false, + resizeable: true, + orderable: true, + renderable: (columnToShow ? _.contains(columnToShow, 'term') : true), + className: 'searchTag', + formatter: _.extend({}, Backgrid.CellFormatter.prototype, { + fromRaw: function(rawValue, model) { + var obj = model.toJSON(); + if (obj.status && Enums.entityStateReadOnly[obj.status]) { + return '<div class="readOnly">' + CommonViewFunction.termForTable(obj); + '</div>'; + } else { + return CommonViewFunction.termForTable(obj); + } + + } + }) + }; + } + }, addTagModalView: function(guid, multiple) { var that = this; require(['views/tag/AddTagModalView'], function(AddTagModalView) { @@ -863,6 +919,19 @@ define(['require', that.addTagModalView(guid); } }, + onClickAddTermBtn: function(e) { + var that = this, + entityGuid = $(e.currentTarget).data("guid"); + require(['views/glossary/AssignTermLayoutView'], function(AssignTermLayoutView) { + var view = new AssignTermLayoutView({ + guid: entityGuid, + callback: function() { + that.fetchCollection(); + }, + glossaryCollection: that.glossaryCollection, + }); + }); + }, onClickTagCross: function(e) { var that = this, tagName = $(e.target).data("name"), @@ -885,6 +954,31 @@ define(['require', }); } }, + onClickTermCross: function(e) { + var $el = $(e.target), + termGuid = $el.data('termguid'), + guid = $el.data('guid'), + termName = $(e.currentTarget).text(), + assetname = $el.data('assetname'), + meanings = this.searchCollection.find({ "guid": guid }).get("meanings"), + that = this, + termObj = _.find(meanings, { termGuid: termGuid }); + CommonViewFunction.removeCategoryTermAssociation({ + termGuid: termGuid, + model: { + guid: guid, + relationshipGuid: termObj.relationGuid + }, + collection: that.glossaryCollection, + msg: "<div class='ellipsis'>Remove: " + "<b>" + _.escape(termName) + "</b> assignment from" + " " + "<b>" + assetname + "?</b></div>", + titleMessage: Messages.glossary.removeTermfromEntity, + isEntityView: true, + buttonText: "Remove", + callback: function() { + that.fetchCollection(); + } + }); + }, deleteTagData: function(options) { var that = this; CommonViewFunction.deleteTag(_.extend({}, options, { diff --git a/dashboardv2/public/js/views/site/Header.js b/dashboardv2/public/js/views/site/Header.js index bd58986..e96559d 100644 --- a/dashboardv2/public/js/views/site/Header.js +++ b/dashboardv2/public/js/views/site/Header.js @@ -36,8 +36,12 @@ define(['require', events['click ' + this.ui.backButton] = function() { var queryParams = Utils.getUrlState.getQueryParams(), urlPath = "searchUrl"; - if (queryParams && queryParams.from && queryParams.from == "classification") { - urlPath = "tagUrl"; + if (queryParams && queryParams.from) { + if (queryParams.from == "classification") { + urlPath = "tagUrl"; + } else if(queryParams.from == "glossary"){ + urlPath = "glossaryUrl"; + } } Utils.setUrl({ url: Globals.saveApplicationState.tabState[urlPath], diff --git a/dashboardv2/public/js/views/site/SideNavLayoutView.js b/dashboardv2/public/js/views/site/SideNavLayoutView.js index b00451b..87cf4e8 100644 --- a/dashboardv2/public/js/views/site/SideNavLayoutView.js +++ b/dashboardv2/public/js/views/site/SideNavLayoutView.js @@ -29,14 +29,14 @@ define(['require', regions: { RTagLayoutView: "#r_tagLayoutView", - RSearchLayoutView: "#r_searchLayoutView" + RSearchLayoutView: "#r_searchLayoutView", + RGlossaryLayoutView: "#r_glossaryLayoutView" }, ui: { tabs: '.tabs li a', }, templateHelpers: function() { return { - tabClass: this.tabClass, apiBaseUrl: UrlLinks.apiBaseUrl }; }, @@ -60,11 +60,14 @@ define(['require', } } - if (elementName.name == "tab-tag") { + if (elementName.name == "tab-classification") { urlString = tabStateUrls.tagUrl; //'#!/tag'; } else if (elementName.name == "tab-search") { urlString = tabStateUrls.searchUrl; // '#!/search'; + } else if (elementName.name == "tab-glossary") { + urlString = tabStateUrls.glossaryUrl; // '#!/glossary'; } + Utils.setUrl({ url: urlString, mergeBrowserUrl: false, @@ -75,40 +78,35 @@ define(['require', return events; }, initialize: function(options) { - _.extend(this, _.pick(options, 'url', 'value', 'tag', 'selectFirst', 'classificationDefCollection', 'typeHeaders', 'searchVent', 'entityDefCollection', 'enumDefCollection', 'searchTableColumns', 'searchTableFilters')); - this.tabClass = "tab col-sm-6"; + this.options = options; }, onRender: function() { this.renderTagLayoutView(); this.renderSearchLayoutView(); + this.renderGlossaryLayoutView(); this.selectTab(); }, renderTagLayoutView: function() { var that = this; require(['views/tag/TagLayoutView'], function(TagLayoutView) { - that.RTagLayoutView.show(new TagLayoutView({ - collection: that.classificationDefCollection, - tag: that.tag, - value: that.value, - enumDefCollection: that.enumDefCollection, - typeHeaders: that.typeHeaders - })); + that.RTagLayoutView.show(new TagLayoutView( + _.extend(that.options, { + "collection": that.options.classificationDefCollection + }) + )); }); }, renderSearchLayoutView: function() { var that = this; require(['views/search/SearchLayoutView'], function(SearchLayoutView) { - that.RSearchLayoutView.show(new SearchLayoutView({ - value: that.value, - searchVent: that.searchVent, - typeHeaders: that.typeHeaders, - entityDefCollection: that.entityDefCollection, - enumDefCollection: that.enumDefCollection, - classificationDefCollection: that.classificationDefCollection, - searchTableColumns: that.searchTableColumns, - searchTableFilters: that.searchTableFilters - })); + that.RSearchLayoutView.show(new SearchLayoutView(that.options)); + }); + }, + renderGlossaryLayoutView: function() { + var that = this; + require(['views/glossary/GlossaryLayoutView'], function(GlossaryLayoutView) { + that.RGlossaryLayoutView.show(new GlossaryLayoutView(that.options)); }); }, selectTab: function() { @@ -121,12 +119,18 @@ define(['require', if (Utils.getUrlState.isSearchTab() || Utils.getUrlState.isInitial()) { activeTab({ "view": "search" }); } else if (Utils.getUrlState.isTagTab()) { - activeTab({ "view": "tag" }); + activeTab({ "view": "classification" }); + } else if (Utils.getUrlState.isGlossaryTab()) { + activeTab({ "view": "glossary" }); } else if (Utils.getUrlState.isDetailPage()) { var queryParams = Utils.getUrlState.getQueryParams(), view = "search"; - if (queryParams && queryParams.from && queryParams.from == "classification") { - view = "tag"; + if (queryParams && queryParams.from) { + if (queryParams.from == "classification") { + view = "tag"; + } else if (queryParams.from == "glossary") { + view = "glossary"; + } } activeTab({ "view": view }); }