ATLAS-2562: UI for Glossary Signed-off-by: Madhan Neethiraj <[email protected]>
Project: http://git-wip-us.apache.org/repos/asf/atlas/repo Commit: http://git-wip-us.apache.org/repos/asf/atlas/commit/3709842a Tree: http://git-wip-us.apache.org/repos/asf/atlas/tree/3709842a Diff: http://git-wip-us.apache.org/repos/asf/atlas/diff/3709842a Branch: refs/heads/master Commit: 3709842a0711b9aef51c0c6c6727015c63379931 Parents: 4ba39dc Author: kevalbhatt <[email protected]> Authored: Tue Apr 17 01:56:57 2018 +0530 Committer: Madhan Neethiraj <[email protected]> Committed: Mon Apr 16 14:18:42 2018 -0700 ---------------------------------------------------------------------- dashboardv2/gruntfile.js | 56 +- dashboardv2/package.json | 3 + dashboardv2/public/css/scss/glossary.scss | 53 ++ dashboardv2/public/css/scss/search.scss | 7 +- dashboardv2/public/css/scss/style.scss | 1 + dashboardv2/public/index.html.tpl | 2 + .../public/js/collection/VGlossaryList.js | 72 +++ dashboardv2/public/js/collection/VSearchList.js | 4 +- dashboardv2/public/js/main.js | 14 +- dashboardv2/public/js/models/VGlossary.js | 111 ++++ dashboardv2/public/js/router/Router.js | 41 +- .../detail_page/DetailPageLayoutView_tmpl.html | 8 + .../glossary/AssignTermLayoutViewTmpl.html | 17 + .../CreateEditCategoryTermLayoutView_tmpl.html | 36 ++ .../CreateEditGlossaryLayoutView_tmpl.html | 36 ++ .../glossary/GlossaryDetailLayoutView_tmpl.html | 67 +++ .../glossary/GlossaryLayoutView_tmpl.html | 52 ++ .../search/SearchResultLayoutView_tmpl.html | 2 + .../templates/site/SideNavLayoutView_tmpl.html | 10 +- .../public/js/utils/CommonViewFunction.js | 217 +++++++- dashboardv2/public/js/utils/Globals.js | 3 +- dashboardv2/public/js/utils/Messages.js | 6 + dashboardv2/public/js/utils/UrlLinks.js | 45 ++ dashboardv2/public/js/utils/Utils.js | 5 + .../views/detail_page/DetailPageLayoutView.js | 73 ++- .../js/views/glossary/AssignTermLayoutView.js | 151 ++++++ .../CreateEditCategoryTermLayoutView.js | 70 +++ .../glossary/CreateEditGlossaryLayoutView.js | 69 +++ .../views/glossary/GlossaryDetailLayoutView.js | 294 +++++++++++ .../js/views/glossary/GlossaryLayoutView.js | 528 +++++++++++++++++++ .../public/js/views/schema/SchemaLayoutView.js | 4 +- .../js/views/search/SearchResultLayoutView.js | 108 +++- dashboardv2/public/js/views/site/Header.js | 8 +- .../public/js/views/site/SideNavLayoutView.js | 54 +- 34 files changed, 2151 insertions(+), 76 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/gruntfile.js ---------------------------------------------------------------------- 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', http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/package.json ---------------------------------------------------------------------- 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", http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/css/scss/glossary.scss ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/css/scss/search.scss ---------------------------------------------------------------------- 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; } http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/css/scss/style.scss ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/index.html.tpl ---------------------------------------------------------------------- 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> http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/collection/VGlossaryList.js ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/collection/VSearchList.js ---------------------------------------------------------------------- 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) { http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/main.js ---------------------------------------------------------------------- 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' }, /** http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/models/VGlossary.js ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/router/Router.js ---------------------------------------------------------------------- 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({ http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html ---------------------------------------------------------------------- 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"> http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/templates/glossary/AssignTermLayoutViewTmpl.html ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/templates/glossary/CreateEditCategoryTermLayoutView_tmpl.html ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/templates/glossary/CreateEditGlossaryLayoutView_tmpl.html ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/templates/glossary/GlossaryDetailLayoutView_tmpl.html ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/templates/glossary/GlossaryLayoutView_tmpl.html ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/templates/search/SearchResultLayoutView_tmpl.html ---------------------------------------------------------------------- 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"> http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/templates/site/SideNavLayoutView_tmpl.html ---------------------------------------------------------------------- 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> http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/utils/CommonViewFunction.js ---------------------------------------------------------------------- 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) { http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/utils/Globals.js ---------------------------------------------------------------------- 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: {} }; http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/utils/Messages.js ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/utils/UrlLinks.js ---------------------------------------------------------------------- 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'; }, http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/utils/Utils.js ---------------------------------------------------------------------- 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"; }, http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js ---------------------------------------------------------------------- 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) { http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/views/glossary/AssignTermLayoutView.js ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/views/glossary/CreateEditCategoryTermLayoutView.js ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/atlas/blob/3709842a/dashboardv2/public/js/views/glossary/CreateEditGlossaryLayoutView.js ---------------------------------------------------------------------- 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
