You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by li...@apache.org on 2016/12/20 11:26:43 UTC
[47/50] [abbrv] kylin git commit: KYLIN-2287 Speed up model and cube
list load in Web
KYLIN-2287 Speed up model and cube list load in Web
Signed-off-by: zhongjian <ji...@163.com>
Project: http://git-wip-us.apache.org/repos/asf/kylin/repo
Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/64a0a594
Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/64a0a594
Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/64a0a594
Branch: refs/heads/master-hbase1.x
Commit: 64a0a5943c034ea6f9cdc6d179d8be9da64fe5ac
Parents: f50c0c8
Author: kangkaisen <ka...@live.com>
Authored: Wed Dec 14 20:08:14 2016 +0800
Committer: zhongjian <ji...@163.com>
Committed: Tue Dec 20 15:12:54 2016 +0800
----------------------------------------------------------------------
webapp/app/js/controllers/cubeSchema.js | 25 +-
webapp/app/js/controllers/cubes.js | 318 ++++++++++-------------
webapp/app/js/controllers/models.js | 54 ++--
webapp/app/js/model/cubeListModel.js | 4 -
webapp/app/js/model/modelsManager.js | 49 +---
webapp/app/partials/cubes/cubes.html | 12 +-
webapp/app/partials/models/models_tree.html | 16 +-
7 files changed, 215 insertions(+), 263 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/kylin/blob/64a0a594/webapp/app/js/controllers/cubeSchema.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/cubeSchema.js b/webapp/app/js/controllers/cubeSchema.js
index 357b6af..af8ee7c 100755
--- a/webapp/app/js/controllers/cubeSchema.js
+++ b/webapp/app/js/controllers/cubeSchema.js
@@ -45,18 +45,6 @@ KylinApp.controller('CubeSchemaCtrl', function ($scope, QueryService, UserServic
$scope.state = {mode: "view"};
}
- var queryParam = {offset: 0, limit: 65535};
-
- CubeService.list(queryParam, function (all_cubes) {
- if($scope.allCubes.length > 0){
- $scope.allCubes.splice(0,$scope.allCubes.length);
- }
-
- for (var i = 0; i < all_cubes.length; i++) {
- $scope.allCubes.push(all_cubes[i].name.toUpperCase());
- }
- });
-
$scope.$watch('cubeMetaFrame', function (newValue, oldValue) {
if(!newValue){
return;
@@ -198,6 +186,19 @@ KylinApp.controller('CubeSchemaCtrl', function ($scope, QueryService, UserServic
};
$scope.check_cube_info = function(){
+
+ var queryParam = {offset: 0, limit: 65535};
+
+ CubeService.list(queryParam, function (all_cubes) {
+ if($scope.allCubes.length > 0){
+ $scope.allCubes.splice(0,$scope.allCubes.length);
+ }
+
+ for (var i = 0; i < all_cubes.length; i++) {
+ $scope.allCubes.push(all_cubes[i].name.toUpperCase());
+ }
+ });
+
if(($scope.state.mode === "edit") &&$scope.cubeMode=="addNewCube"&&($scope.allCubes.indexOf($scope.cubeMetaFrame.name.toUpperCase()) >= 0)){
SweetAlert.swal('Oops...', "The cube named [" + $scope.cubeMetaFrame.name.toUpperCase() + "] already exists", 'warning');
return false;
http://git-wip-us.apache.org/repos/asf/kylin/blob/64a0a594/webapp/app/js/controllers/cubes.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/cubes.js b/webapp/app/js/controllers/cubes.js
index b672d3f..cbdbb7d 100644
--- a/webapp/app/js/controllers/cubes.js
+++ b/webapp/app/js/controllers/cubes.js
@@ -79,43 +79,6 @@ KylinApp.controller('CubesCtrl', function ($scope, $q, $routeParams, $location,
return CubeList.list(queryParam).then(function (resp) {
angular.forEach($scope.cubeList.cubes,function(cube,index){
- cube.streaming = false;
- CubeDescService.query({cube_name: cube.name}, {}, function (detail) {
- if (detail.length > 0 && detail[0].hasOwnProperty("name")) {
- cube.detail = detail[0];
- ModelService.list({projectName:$scope.projectModel.selectedProject,modelName:cube.detail.model_name}, function (_models) {
- if(_models && _models.length){
- for(var i=0;i<=_models.length;i++){
- if(_models[i].name == cube.detail.model_name){
- cube.model = _models[i];
- var factTable = cube.model.fact_table;
- TableService.get({tableName:factTable},function(table){
- if(table && table.source_type == 1){
- cube.streaming = true;
- }
- })
- break;
- }
- }
- }
-
- })
- //cube.model = modelsManager.getModel(cube.detail.model_name);
-
- defer.resolve(cube.detail);
-
- } else {
- SweetAlert.swal('Oops...', "No cube detail info loaded.", 'error');
- }
- }, function (e) {
- if (e.data && e.data.exception) {
- var message = e.data.exception;
- var msg = !!(message) ? message : 'Failed to take action.';
- SweetAlert.swal('Oops...', msg, 'error');
- } else {
- SweetAlert.swal('Oops...', "Failed to take action.", 'error');
- }
- });
})
$scope.loading = false;
@@ -331,140 +294,139 @@ KylinApp.controller('CubesCtrl', function ($scope, $q, $routeParams, $location,
};
$scope.startJobSubmit = function (cube) {
- $scope.loadDetail(cube);
- // for streaming cube build tip
- if(cube.streaming){
-
- SweetAlert.swal({
- title: '',
- text: "Are you sure to start the build?",
- type: '',
- showCancelButton: true,
- confirmButtonColor: '#DD6B55',
- confirmButtonText: "Yes",
- closeOnConfirm: true
- }, function(isConfirm) {
- if(isConfirm){
- loadingRequest.show();
- CubeService.rebuildStreamingCube(
- {
- cubeId: cube.name
- },
- {
- sourceOffsetStart:0,
- sourceOffsetEnd:'9223372036854775807',
- buildType:'BUILD'
- }, function (job) {
- loadingRequest.hide();
- SweetAlert.swal('Success!', 'Rebuild job was submitted successfully', 'success');
- },function(e){
-
- loadingRequest.hide();
- if(e.data&& e.data.exception){
- var message =e.data.exception;
- var msg = !!(message) ? message : 'Failed to take action.';
- SweetAlert.swal('Oops...', msg, 'error');
- }else{
- SweetAlert.swal('Oops...', "Failed to take action.", 'error');
+
+ $scope.loadDetail(cube).then(function () {
+ $scope.metaModel={
+ model:cube.model
+ };
+
+ TableService.get({tableName:$scope.metaModel.model.fact_table},function(table){
+ if(table && table.source_type == 1){
+ cube.streaming = true;
+ }
+
+ // for streaming cube build tip
+ if(cube.streaming){
+ $modal.open({
+ templateUrl: 'streamingBuild.html',
+ controller: streamingBuildCtrl,
+ resolve: {
+ cube: function () {
+ return cube;
+ },
+ metaModel:function(){
+ return $scope.metaModel;
+ },
+ buildType: function () {
+ return 'BUILD';
+ },
+ scope:function(){
+
+ return $scope;
}
+ }
});
+ return;
}
- })
- return;
- }
- $scope.metaModel={
- model:modelsManager.getModelByCube(cube.name)
- }
- if ($scope.metaModel.model.name) {
- if ($scope.metaModel.model.partition_desc.partition_date_column) {
-
- $modal.open({
- templateUrl: 'jobSubmit.html',
- controller: jobSubmitCtrl,
- resolve: {
- cube: function () {
- return cube;
- },
- metaModel:function(){
- return $scope.metaModel;
- },
- buildType: function () {
- return 'BUILD';
- },
- scope:function(){
-
- return $scope;
- }
+ //for batch cube build tip
+ if ($scope.metaModel.model.name) {
+
+ //for partition cube build tip
+ if ($scope.metaModel.model.partition_desc.partition_date_column) {
+ $modal.open({
+ templateUrl: 'jobSubmit.html',
+ controller: jobSubmitCtrl,
+ resolve: {
+ cube: function () {
+ return cube;
+ },
+ metaModel:function(){
+ return $scope.metaModel;
+ },
+ buildType: function () {
+ return 'BUILD';
+ },
+ scope:function(){
+ return $scope;
+ }
+ }
+ });
}
- });
- }
- else {
- SweetAlert.swal({
- title: '',
- text: "Are you sure to start the build ?",
- type: '',
- showCancelButton: true,
- confirmButtonColor: '#DD6B55',
- confirmButtonText: "Yes",
- closeOnConfirm: true
- }, function(isConfirm) {
- if(isConfirm){
+ //for not partition cube build tip
+ else {
+ SweetAlert.swal({
+ title: '',
+ text: "Are you sure to start the build ?",
+ type: '',
+ showCancelButton: true,
+ confirmButtonColor: '#DD6B55',
+ confirmButtonText: "Yes",
+ closeOnConfirm: true
+ }, function(isConfirm) {
+ if(isConfirm){
+
+ loadingRequest.show();
+ CubeService.rebuildCube(
+ {
+ cubeId: cube.name
+ },
+ {
+ buildType: 'BUILD',
+ startTime: 0,
+ endTime: 0
+ }, function (job) {
+
+ loadingRequest.hide();
+ SweetAlert.swal('Success!', 'Rebuild job was submitted successfully', 'success');
+ },function(e){
+
+ loadingRequest.hide();
+ if(e.data&& e.data.exception){
+ var message =e.data.exception;
+ var msg = !!(message) ? message : 'Failed to take action.';
+ SweetAlert.swal('Oops...', msg, 'error');
+ }else{
+ SweetAlert.swal('Oops...', "Failed to take action.", 'error');
+ }
+ });
+ }
- loadingRequest.show();
- CubeService.rebuildCube(
- {
- cubeId: cube.name
- },
- {
- buildType: 'BUILD',
- startTime: 0,
- endTime: 0
- }, function (job) {
-
- loadingRequest.hide();
- SweetAlert.swal('Success!', 'Rebuild job was submitted successfully', 'success');
- },function(e){
-
- loadingRequest.hide();
- if(e.data&& e.data.exception){
- var message =e.data.exception;
- var msg = !!(message) ? message : 'Failed to take action.';
- SweetAlert.swal('Oops...', msg, 'error');
- }else{
- SweetAlert.swal('Oops...', "Failed to take action.", 'error');
- }
- });
+ });
}
+ }
+ })
+ })
- });
- }
- }
};
$scope.startRefresh = function (cube) {
- $scope.metaModel={
- model:modelsManager.getModelByCube(cube.name)
- };
- $modal.open({
- templateUrl: 'jobRefresh.html',
- controller: jobSubmitCtrl,
- resolve: {
- cube: function () {
- return cube;
- },
- metaModel:function(){
- return $scope.metaModel;
- },
- buildType: function () {
- return 'REFRESH';
- },
- scope:function(){
- return $scope;
+
+ $scope.loadDetail(cube).then(function () {
+ $scope.metaModel={
+ model:cube.model
+ };
+ $modal.open({
+ templateUrl: 'jobRefresh.html',
+ controller: jobSubmitCtrl,
+ resolve: {
+ cube: function () {
+ return cube;
+ },
+ metaModel:function(){
+ return $scope.metaModel;
+ },
+ buildType: function () {
+ return 'REFRESH';
+ },
+ scope:function(){
+ return $scope;
+ }
}
+ });
}
- });
+ )
};
@@ -493,27 +455,29 @@ KylinApp.controller('CubesCtrl', function ($scope, $q, $routeParams, $location,
$location.path("cubes/edit/" + cube.name);
}
$scope.startMerge = function (cube) {
- $scope.metaModel={
- model:modelsManager.getModelByCube(cube.name)
- };
- $modal.open({
- templateUrl: 'jobMerge.html',
- controller: jobSubmitCtrl,
- resolve: {
- cube: function () {
- return cube;
- },
- metaModel:function(){
- return $scope.metaModel;
- },
- buildType: function () {
- return 'MERGE';
- },
- scope:function(){
- return $scope;
+ $scope.loadDetail(cube).then(function () {
+ $scope.metaModel={
+ model:cube.model
+ };
+ $modal.open({
+ templateUrl: 'jobMerge.html',
+ controller: jobSubmitCtrl,
+ resolve: {
+ cube: function () {
+ return cube;
+ },
+ metaModel:function(){
+ return $scope.metaModel;
+ },
+ buildType: function () {
+ return 'MERGE';
+ },
+ scope:function(){
+ return $scope;
+ }
}
- }
- });
+ });
+ })
}
});
http://git-wip-us.apache.org/repos/asf/kylin/blob/64a0a594/webapp/app/js/controllers/models.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/models.js b/webapp/app/js/controllers/models.js
index fb2c6d1..e3ee038 100644
--- a/webapp/app/js/controllers/models.js
+++ b/webapp/app/js/controllers/models.js
@@ -18,7 +18,7 @@
'use strict';
-KylinApp.controller('ModelsCtrl', function ($scope, $q, $routeParams, $location, $window, $modal, MessageService, CubeDescService, CubeService, JobService, UserService, ProjectService, SweetAlert, loadingRequest, $log, modelConfig, ProjectModel, ModelService, MetaModel, modelsManager, cubesManager, TableModel, $animate) {
+KylinApp.controller('ModelsCtrl', function ($scope, $q, $routeParams, $location, $window, $modal, MessageService, CubeDescService, CubeService, JobService, UserService, ProjectService, SweetAlert, loadingRequest, $log, modelConfig, ProjectModel, ModelService, MetaModel, modelsManager, cubesManager, TableModel, AccessService) {
//tree data
@@ -131,21 +131,28 @@ KylinApp.controller('ModelsCtrl', function ($scope, $q, $routeParams, $location,
var cubename = [];
var modelstate=false;
var i=0;
- if (model.cubes.length != 0) {
- angular.forEach(model.cubes,function(cube){
- if (cube.status=="READY"){
- modelstate=true;
- cubename[i] =cube.name;
- i++;
- }
- })
- }
- if(modelstate==false){
- $location.path("/models/edit/"+model.name);
- }
- else{
- SweetAlert.swal('Sorry','This model is still used by '+ cubename.join(','));
- }
+
+ CubeService.list({modelName:model.name}, function (_cubes) {
+ model.cubes = _cubes;
+
+ if (model.cubes.length != 0) {
+ angular.forEach(model.cubes,function(cube){
+ if (cube.status=="READY"){
+ modelstate=true;
+ cubename[i] =cube.name;
+ i++;
+ }
+ })
+ }
+
+ if(modelstate==false){
+ $location.path("/models/edit/"+model.name);
+ }
+ else{
+ SweetAlert.swal('Sorry','This model is still used by '+ cubename.join(','));
+ }
+ })
+
};
$scope.cloneModel = function(model){
@@ -176,6 +183,21 @@ KylinApp.controller('ModelsCtrl', function ($scope, $q, $routeParams, $location,
});
};
+ $scope.listModelAccess = function (model) {
+ if(model.uuid){
+ AccessService.list({type: "DataModelDesc", uuid: model.uuid}, function (accessEntities) {
+ model.accessEntities = accessEntities;
+ try {
+ if (!model.owner) {
+ model.owner = accessEntities[0].sid.principal;
+ }
+ } catch (error) {
+ $log.error("No acl info.");
+ }
+ })
+ }
+ };
+
var ModelDetailModalCtrl = function ($scope, $location, $modalInstance, scope) {
modelsManager.selectedModel.visiblePage='metadata';
$scope.cancel = function () {
http://git-wip-us.apache.org/repos/asf/kylin/blob/64a0a594/webapp/app/js/model/cubeListModel.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/model/cubeListModel.js b/webapp/app/js/model/cubeListModel.js
index 13c924c..39853c0 100755
--- a/webapp/app/js/model/cubeListModel.js
+++ b/webapp/app/js/model/cubeListModel.js
@@ -25,10 +25,6 @@ KylinApp.service('CubeList',function(CubeService,$q,AccessService){
var defer = $q.defer();
CubeService.list(queryParam, function (_cubes) {
angular.forEach(_cubes, function (cube, index) {
- AccessService.list({type: "CubeInstance", uuid: cube.uuid}, function (accessEntities) {
- cube.accessEntities = accessEntities;
- });
-
if(cube.name){
if (cube.segments && cube.segments.length > 0) {
for(var i= cube.segments.length-1;i>=0;i--){
http://git-wip-us.apache.org/repos/asf/kylin/blob/64a0a594/webapp/app/js/model/modelsManager.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/model/modelsManager.js b/webapp/app/js/model/modelsManager.js
index c104405..faa8d39 100644
--- a/webapp/app/js/model/modelsManager.js
+++ b/webapp/app/js/model/modelsManager.js
@@ -25,56 +25,22 @@ KylinApp.service('modelsManager',function(ModelService,CubeService,$q,AccessServ
this.loading = false;
this.selectedModel={};
- this.cubeModel={};
- this.cubeSelected = false;
-
- //list models and complemete cube,access info
+ //list models
this.list = function(queryParam){
_this.loading = true;
var defer = $q.defer();
- var cubeDetail = [];
- var modelPermission = [];
ModelService.list(queryParam, function (_models) {
- //_this.removeAll();
angular.forEach(_models, function (model, index) {
- $log.info("Add model permission info");
- if(model.uuid){
- modelPermission.push(
- AccessService.list({type: "DataModelDesc", uuid: model.uuid}, function (accessEntities) {
- model.accessEntities = accessEntities;
- try{
- if(!model.owner){
- model.owner = accessEntities[0].sid.principal;
- }
- } catch(error){
- $log.error("No acl info.");
- }
-
- }).$promise
- )
- }
-
- $log.info("Add cube info to model ,not detail info");
- cubeDetail.push(
- CubeService.list({modelName:model.name}, function (_cubes) {
- model.cubes = _cubes;
- }).$promise
- );
-
_this.modelNameList.push(model.name);
-
- model.project = ProjectModel.getProjectByCubeModel(model.name);
+ model.project = ProjectModel.getProjectByCubeModel(model.name);
});
- $q.all(cubeDetail,modelPermission).then(
- function(result){
- _models = _.filter(_models,function(models){return models.name!=undefined});
- _this.models = _models;
- _this.loading = false;
- defer.resolve(_this.models);
- }
- );
+
+ _models = _.filter(_models,function(models){return models.name!=undefined});
+ _this.models = _models;
+ _this.loading = false;
+
},function(){
defer.reject("Failed to load models");
});
@@ -127,5 +93,4 @@ KylinApp.service('modelsManager',function(ModelService,CubeService,$q,AccessServ
return defer.promise;
};
-
});
http://git-wip-us.apache.org/repos/asf/kylin/blob/64a0a594/webapp/app/partials/cubes/cubes.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/cubes/cubes.html b/webapp/app/partials/cubes/cubes.html
index 0187523..4be7ecd 100644
--- a/webapp/app/partials/cubes/cubes.html
+++ b/webapp/app/partials/cubes/cubes.html
@@ -54,7 +54,6 @@
</th>
<th>Actions</th>
<th ng-if="userService.hasRole('ROLE_ADMIN')">Admins</th>
- <th>Streaming</th>
</tr>
</thead>
<!--Body-->
@@ -83,12 +82,12 @@
<td>{{ cube.owner}}</td>
<td>{{ cube.create_time_utc | utcToConfigTimeZone}}</td>
<td>
- <div ng-click="$event.stopPropagation();" class="btn-group" ng-if="userService.hasRole('ROLE_ADMIN') || hasPermission(cube, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask, permissions.OPERATION.mask)">
+ <div ng-click="$event.stopPropagation();" class="btn-group">
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
data-toggle="dropdown" ng-click="listAccess(cube, 'CubeInstance')">
Action <span class="ace-icon fa fa-caret-down icon-on-right"></span>
</button>
- <ul class="dropdown-menu" role="menu">
+ <ul ng-if="userService.hasRole('ROLE_ADMIN') || hasPermission(cube, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask, permissions.OPERATION.mask)" class="dropdown-menu" role="menu">
<li ng-if="cube.status!='READY' && userService.hasRole('ROLE_ADMIN') ">
<a ng-click="dropCube(cube)" tooltip="Drop the cube, related jobs and data permanently.">Drop</a></li>
<li ng-if="(userService.hasRole('ROLE_ADMIN') || hasPermission(cube, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask))">
@@ -108,20 +107,17 @@
N/A
</span>
</td>
- <td ng-if="userService.hasRole('ROLE_ADMIN')">
+ <td>
<div ng-click="$event.stopPropagation();" class="btn-group">
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" ng-click="listAccess(cube, 'CubeInstance')">
Action <span class="ace-icon fa fa-caret-down icon-on-right"></span>
</button>
- <ul class="dropdown-menu" role="menu">
+ <ul class="dropdown-menu" role="menu" ng-if="userService.hasRole('ROLE_ADMIN')">
<li ng-if="cube.status!='READY'"><a href="cubes/edit/{{cube.name}}/descriptionjson">Edit CubeDesc</a></li>
<li><a href="cubes/view/{{cube.name}}/instancejson">View Cube</a></li>
</ul>
</div>
</td>
- <td>
- <label class="badge" ng-class="{'label-info':cube.streaming==true}" style="cursor:pointer;">{{cube.streaming}}</label>
- </td>
</tr>
<tr ng-show="cube.showDetail">
<td colspan="10" style="padding: 10px 30px 10px 30px;">
http://git-wip-us.apache.org/repos/asf/kylin/blob/64a0a594/webapp/app/partials/models/models_tree.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/models/models_tree.html b/webapp/app/partials/models/models_tree.html
index 530139e..944d2dc 100644
--- a/webapp/app/partials/models/models_tree.html
+++ b/webapp/app/partials/models/models_tree.html
@@ -41,15 +41,23 @@
<div>
<h3 class="text-info">Models</h3>
</div>
+
<div style="width:100%; height:{{window}}px; overflow:auto;margin-top: 20px;" class="cube_model_trees">
<ul class="list-group models-tree">
<li class="list-group-item" ng-repeat="model in modelsManager.models">
- <div class="pull-right" showonhoverparent style="display:none;">
- <a ng-click="editModel(model)" title="Edit Model" style="cursor:pointer;margin-right: 8px;" ng-if="(userService.hasRole('ROLE_ADMIN') || hasPermission(model, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask, permissions.OPERATION.mask))"><span class="fa fa-pencil fa-lg fa-fw"></span></a>
- <a ng-click="cloneModel(model)" title="Clone Model" style="cursor:pointer;margin-right: 8px;" ng-if="(userService.hasRole('ROLE_ADMIN') || hasPermission(model, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask, permissions.OPERATION.mask))"><span class="fa fa-copy fa-lg fa-fw"></span></a>
- <a ng-click="dropModel(model)" title="Drop Model" style="cursor:pointer;margin-right: 8px;" ng-if="(userService.hasRole('ROLE_ADMIN') || hasPermission(model, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask, permissions.OPERATION.mask))"><span class="fa fa-trash-o fa-lg fa-fw"></span></a>
+ <div class="pull-right" showonhoverparent style="display:none;" >
+ <div ng-click="$event.stopPropagation();" class="btn-group">
+ <button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" ng-click="listModelAccess(model)">
+ Action <span class="ace-icon fa fa-caret-down icon-on-right"></span>
+ </button>
+ <ul class="dropdown-menu" role="menu" ng-if="(userService.hasRole('ROLE_ADMIN') || hasPermission(model, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask, permissions.OPERATION.mask))">
+ <li><a ng-click="editModel(model)" title="Edit Model" style="cursor:pointer;margin-right: 8px;" >Edit</a></li>
+ <li><a ng-click="cloneModel(model)" title="Clone Model" style="cursor:pointer;margin-right: 8px;" >Clone </a></li>
+ <li><a ng-click="dropModel(model)" title="Drop Model" style="cursor:pointer;margin-right: 8px;">Drop</a></li>
+ </ul>
+ </div>
</div>
<span class="strong"><a style="cursor: pointer;word-break:break-all;" ng-click="openModal(model)">{{model.name}}</a></span>