diff --git a/Deploy/templates/jobservice/env b/Deploy/templates/jobservice/env index 9422721..5359e85 100644 --- a/Deploy/templates/jobservice/env +++ b/Deploy/templates/jobservice/env @@ -8,6 +8,7 @@ REGISTRY_URL=http://registry:5000 VERIFY_REMOTE_CERT=$verify_remote_cert MAX_JOB_WORKERS=$max_job_workers LOG_LEVEL=debug +LOG_DIR=/var/log/jobs GODEBUG=netdns=cgo EXT_ENDPOINT=$ui_url TOKEN_URL=http://ui diff --git a/dao/dao_test.go b/dao/dao_test.go index 5a17986..6992247 100644 --- a/dao/dao_test.go +++ b/dao/dao_test.go @@ -1139,24 +1139,40 @@ func TestGetRepPolicyByProject(t *testing.T) { func TestGetRepJobByPolicy(t *testing.T) { jobs, err := GetRepJobByPolicy(999) if err != nil { - log.Errorf("Error occured in GetRepJobByPolicy: %v, policy ID: %d", err, 999) + t.Errorf("Error occured in GetRepJobByPolicy: %v, policy ID: %d", err, 999) return } if len(jobs) > 0 { - log.Errorf("Unexpected length of jobs, expected: 0, in fact: %d", len(jobs)) + t.Errorf("Unexpected length of jobs, expected: 0, in fact: %d", len(jobs)) return } jobs, err = GetRepJobByPolicy(policyID) if err != nil { - log.Errorf("Error occured in GetRepJobByPolicy: %v, policy ID: %d", err, policyID) + t.Errorf("Error occured in GetRepJobByPolicy: %v, policy ID: %d", err, policyID) return } if len(jobs) != 1 { - log.Errorf("Unexpected length of jobs, expected: 1, in fact: %d", len(jobs)) + t.Errorf("Unexpected length of jobs, expected: 1, in fact: %d", len(jobs)) return } if jobs[0].ID != jobID { - log.Errorf("Unexpected job ID in the result, expected: %d, in fact: %d", jobID, jobs[0].ID) + t.Errorf("Unexpected job ID in the result, expected: %d, in fact: %d", jobID, jobs[0].ID) + return + } +} + +func TestFilterRepJobs(t *testing.T) { + jobs, err := FilterRepJobs(policyID, "", "", nil, nil, 1000) + if err != nil { + t.Errorf("Error occured in FilterRepJobs: %v, policy ID: %d", err, policyID) + return + } + if len(jobs) != 1 { + t.Errorf("Unexpected length of jobs, expected: 1, in fact: %d", len(jobs)) + return + } + if jobs[0].ID != jobID { + t.Errorf("Unexpected job ID in the result, expected: %d, in fact: %d", jobID, jobs[0].ID) return } } @@ -1179,22 +1195,6 @@ func TestDeleteRepJob(t *testing.T) { } } -func TestFilterRepJobs(t *testing.T) { - jobs, err := FilterRepJobs(policyID, "", "", nil, nil, 1000) - if err != nil { - log.Errorf("Error occured in FilterRepJobs: %v, policy ID: %d", err, policyID) - return - } - if len(jobs) != 1 { - log.Errorf("Unexpected length of jobs, expected: 1, in fact: %d", len(jobs)) - return - } - if jobs[0].ID != jobID { - log.Errorf("Unexpected job ID in the result, expected: %d, in fact: %d", jobID, jobs[0].ID) - return - } -} - func TestGetRepoJobToStop(t *testing.T) { jobs := [...]models.RepJob{ models.RepJob{ @@ -1265,7 +1265,7 @@ func TestDeleteRepTarget(t *testing.T) { func TestFilterRepPolicies(t *testing.T) { _, err := FilterRepPolicies("name", 0) if err != nil { - t.Fatalf("failed to filter policy") + t.Fatalf("failed to filter policy: %v", err) } } diff --git a/dao/replication_job.go b/dao/replication_job.go index f0d4782..adc4739 100644 --- a/dao/replication_job.go +++ b/dao/replication_job.go @@ -150,10 +150,13 @@ func FilterRepPolicies(name string, projectID int64) ([]*models.RepPolicy, error sql := `select rp.id, rp.project_id, p.name as project_name, rp.target_id, rt.name as target_name, rp.name, rp.enabled, rp.description, - rp.cron_str, rp.start_time, rp.creation_time, rp.update_time + rp.cron_str, rp.start_time, rp.creation_time, rp.update_time, + count(rj.status) as error_job_count from replication_policy rp - join project p on rp.project_id=p.project_id - join replication_target rt on rp.target_id=rt.id ` + left join project p on rp.project_id=p.project_id + left join replication_target rt on rp.target_id=rt.id + left join replication_job rj on rp.id=rj.policy_id and (rj.status="error" + or rj.status="retrying") ` if len(name) != 0 && projectID != 0 { sql += `where rp.name like ? and rp.project_id = ? ` @@ -167,7 +170,7 @@ func FilterRepPolicies(name string, projectID int64) ([]*models.RepPolicy, error args = append(args, projectID) } - sql += `order by rp.creation_time` + sql += `group by rp.id order by rp.creation_time` var policies []*models.RepPolicy if _, err := o.Raw(sql, args).QueryRows(&policies); err != nil { diff --git a/job/replication/error.go b/job/replication/error.go index 197dabb..19eedd1 100644 --- a/job/replication/error.go +++ b/job/replication/error.go @@ -23,7 +23,7 @@ func retry(err error) bool { if err == nil { return false } - return isTemporary(err) + return isNetworkErr(err) } func isTemporary(err error) bool { @@ -32,3 +32,8 @@ func isTemporary(err error) bool { } return false } + +func isNetworkErr(err error) bool { + _, ok := err.(net.Error) + return ok +} diff --git a/models/replication_job.go b/models/replication_job.go index 59c2ad3..8d847f9 100644 --- a/models/replication_job.go +++ b/models/replication_job.go @@ -56,12 +56,13 @@ type RepPolicy struct { TargetName string `json:"target_name,omitempty"` Name string `orm:"column(name)" json:"name"` // Target RepTarget `orm:"-" json:"target"` - Enabled int `orm:"column(enabled)" json:"enabled"` - Description string `orm:"column(description)" json:"description"` - CronStr string `orm:"column(cron_str)" json:"cron_str"` - StartTime time.Time `orm:"column(start_time)" json:"start_time"` - CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"` - UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"` + Enabled int `orm:"column(enabled)" json:"enabled"` + Description string `orm:"column(description)" json:"description"` + CronStr string `orm:"column(cron_str)" json:"cron_str"` + StartTime time.Time `orm:"column(start_time)" json:"start_time"` + CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"` + UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"` + ErrorJobCount int `json:"error_job_count"` } // Valid ... diff --git a/static/resources/css/repository.css b/static/resources/css/repository.css index 6c927ec..29fe870 100644 --- a/static/resources/css/repository.css +++ b/static/resources/css/repository.css @@ -110,20 +110,34 @@ } .popover-header { - padding:8px 14px; - background-color:#f7f7f7; - border-bottom:1px solid #ebebeb; - -webkit-border-radius:5px 5px 0 0; - -moz-border-radius:5px 5px 0 0; - border-radius:5px 5px 0 0; + padding:8px 14px; + background-color:#f7f7f7; + border-bottom:1px solid #ebebeb; + -webkit-border-radius:5px 5px 0 0; + -moz-border-radius:5px 5px 0 0; + border-radius:5px 5px 0 0; } .popover-title { - height: 2.5em; - padding: 8px 14px; - margin: 0; - font-size: 14px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-radius: 5px 5px 0 0; + height: 2.5em; + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; } + +.alert-custom { + position: fixed; + bottom: 24px; + z-index: 99; + width: 1110px; + padding: 10px; + background-color: #f2dede; + background-image: none; +} + +.alert-custom .close { + right: 0; +} \ No newline at end of file diff --git a/static/resources/js/components/details/retrieve-projects.directive.js b/static/resources/js/components/details/retrieve-projects.directive.js index 8ed1d9a..9d8e873 100644 --- a/static/resources/js/components/details/retrieve-projects.directive.js +++ b/static/resources/js/components/details/retrieve-projects.directive.js @@ -44,7 +44,7 @@ } function getProjectSuccess(data, status) { - vm.projects = data; + vm.projects = data || []; if(!angular.isDefined(vm.projects)) { vm.isPublic = 1; @@ -77,11 +77,11 @@ }); } - function getProjectFailed(response) { - $scope.$emit('modalTitle', $filter('tr')('error')); - $scope.$emit('modalMessage', $filter('tr')('failed_to_get_project')); - $scope.$emit('raiseError', true); - console.log('Failed to list projects:' + response); + function getProjectFailed() { +// $scope.$emit('modalTitle', $filter('tr')('error')); +// $scope.$emit('modalMessage', $filter('tr')('failed_to_get_project')); +// $scope.$emit('raiseError', true); + console.log('Failed to list projects.'); } function selectItem(item) { @@ -91,7 +91,6 @@ $scope.$on('$locationChangeSuccess', function(e) { var projectId = getParameterByName('project_id', $location.absUrl()); - vm.checkProjectMember(projectId); vm.isOpen = false; }); @@ -107,12 +106,7 @@ } function getCurrentProjectMemberFailed(data, status) { - vm.isProjectMember = false; - -// $scope.$emit('modalTitle', $filter('tr')('error')); -// $scope.$emit('modalMessage', $filter('tr')('failed_to_get_project_member')); -// $scope.$emit('raiseError', true); - + vm.isProjectMember = false; console.log('Current user has no member for the project:' + status + ', location.url:' + $location.url()); } diff --git a/static/resources/js/components/dismissable-alerts/dismissable-alerts.directive.html b/static/resources/js/components/dismissable-alerts/dismissable-alerts.directive.html new file mode 100644 index 0000000..fb0bd2a --- /dev/null +++ b/static/resources/js/components/dismissable-alerts/dismissable-alerts.directive.html @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/static/resources/js/components/dismissable-alerts/dismissable-alerts.directive.js b/static/resources/js/components/dismissable-alerts/dismissable-alerts.directive.js new file mode 100644 index 0000000..09bfa05 --- /dev/null +++ b/static/resources/js/components/dismissable-alerts/dismissable-alerts.directive.js @@ -0,0 +1,34 @@ +(function() { + + 'use strict'; + + angular + .module('harbor.dismissable.alerts') + .directive('dismissableAlerts', dismissableAlerts); + + function dismissableAlerts() { + var directive = { + 'restrict': 'E', + 'templateUrl': '/static/resources/js/components/dismissable-alerts/dismissable-alerts.directive.html', + 'link': link + }; + return directive; + function link(scope, element, attrs, ctrl) { + + scope.close = function() { + scope.toggleAlert = false; + } + scope.$on('raiseAlert', function(e, val) { + console.log('received raiseAlert:' + angular.toJson(val)); + if(val.show) { + scope.message = val.message; + scope.toggleAlert = true; + }else{ + scope.message = '' + scope.toggleAlert = false; + } + }); + } + } + +})(); \ No newline at end of file diff --git a/static/resources/js/components/dismissable-alerts/dismissable-alerts.module.js b/static/resources/js/components/dismissable-alerts/dismissable-alerts.module.js new file mode 100644 index 0000000..d6a7e61 --- /dev/null +++ b/static/resources/js/components/dismissable-alerts/dismissable-alerts.module.js @@ -0,0 +1,7 @@ +(function() { + + 'use strict'; + + angular.module('harbor.dismissable.alerts', []); + +})(); \ No newline at end of file diff --git a/static/resources/js/components/inline-help/inline-help.directive.html b/static/resources/js/components/inline-help/inline-help.directive.html index 3855f53..4d32d29 100644 --- a/static/resources/js/components/inline-help/inline-help.directive.html +++ b/static/resources/js/components/inline-help/inline-help.directive.html @@ -1,5 +1,3 @@ - + \ No newline at end of file diff --git a/static/resources/js/components/log/list-log.directive.js b/static/resources/js/components/log/list-log.directive.js index 567f035..ece3190 100644 --- a/static/resources/js/components/log/list-log.directive.js +++ b/static/resources/js/components/log/list-log.directive.js @@ -37,9 +37,15 @@ 'projectId': vm.projectId, 'username' : vm.username }; + retrieve(vm.queryParams); $scope.$on('$locationChangeSuccess', function() { + + if(vm.publicity) { + vm.target = 'repositories'; + } + vm.projectId = getParameterByName('project_id', $location.absUrl()); vm.queryParams = { 'beginTimestamp' : vm.beginTimestamp, @@ -128,7 +134,9 @@ restrict: 'E', templateUrl: '/static/resources/js/components/log/list-log.directive.html', scope: { - 'sectionHeight': '=' + 'sectionHeight': '=', + 'target': '=', + 'publicity': '=' }, controller: ListLogController, controllerAs: 'vm', diff --git a/static/resources/js/components/project-member/edit-project-member.directive.html b/static/resources/js/components/project-member/edit-project-member.directive.html index 7dff549..09627f5 100644 --- a/static/resources/js/components/project-member/edit-project-member.directive.html +++ b/static/resources/js/components/project-member/edit-project-member.directive.html @@ -2,7 +2,7 @@ - + diff --git a/static/resources/js/components/project-member/list-project-member.directive.js b/static/resources/js/components/project-member/list-project-member.directive.js index 77415f6..7c537ca 100644 --- a/static/resources/js/components/project-member/list-project-member.directive.js +++ b/static/resources/js/components/project-member/list-project-member.directive.js @@ -22,7 +22,7 @@ vm.deleteProjectMember = deleteProjectMember; vm.retrieve = retrieve; vm.username = ''; - + vm.projectId = getParameterByName('project_id', $location.absUrl()); vm.retrieve(); @@ -78,11 +78,7 @@ function getProjectMemberFailed(response) { console.log('Failed to get project members:' + response); vm.projectMembers = []; - - $scope.$emit('modalTitle', $filter('tr')('error')); - $scope.$emit('modalMessage', $filter('tr')('failed_to_get_project_member')); - $scope.$emit('raiseError', true); - + vm.target = 'repositories'; $location.url('repositories').search('project_id', vm.projectId); } @@ -93,7 +89,8 @@ 'restrict': 'E', 'templateUrl': '/static/resources/js/components/project-member/list-project-member.directive.html', 'scope': { - 'sectionHeight': '=' + 'sectionHeight': '=', + 'target': '=' }, 'controller': ListProjectMemberController, 'controllerAs': 'vm', diff --git a/static/resources/js/components/project/add-project.directive.html b/static/resources/js/components/project/add-project.directive.html index 16fa3c9..52c0d50 100644 --- a/static/resources/js/components/project/add-project.directive.html +++ b/static/resources/js/components/project/add-project.directive.html @@ -14,7 +14,7 @@
 // 'public' | tr // - +
diff --git a/static/resources/js/components/project/publicity-button.directive.js b/static/resources/js/components/project/publicity-button.directive.js index 5d86eb0..84c1f43 100644 --- a/static/resources/js/components/project/publicity-button.directive.js +++ b/static/resources/js/components/project/publicity-button.directive.js @@ -6,19 +6,38 @@ .module('harbor.project') .directive('publicityButton', publicityButton); - PublicityButtonController.$inject = ['ToggleProjectPublicityService']; + PublicityButtonController.$inject = ['$scope', 'ToggleProjectPublicityService', '$filter', 'trFilter']; - function PublicityButtonController(ToggleProjectPublicityService) { + function PublicityButtonController($scope, ToggleProjectPublicityService, $filter, trFilter) { var vm = this; vm.toggle = toggle; - if(vm.isPublic === 1) { - vm.isPublic = true; - }else{ - vm.isPublic = false; + function toggle() { + if(vm.isPublic) { + vm.isPublic = false; + }else{ + vm.isPublic = true; + } + ToggleProjectPublicityService(vm.projectId, vm.isPublic) + .success(toggleProjectPublicitySuccess) + .error(toggleProjectPublicityFailed); } - - function toggle() { + + function toggleProjectPublicitySuccess(data, status) { + + console.log('Successful toggle project publicity.'); + } + + function toggleProjectPublicityFailed(e, status) { + $scope.$emit('modalTitle', $filter('tr')('error')); + var message; + if(status === 403) { + message = $filter('tr')('failed_to_toggle_publicity_insuffient_permissions'); + }else{ + message = $filter('tr')('failed_to_toggle_publicity'); + } + $scope.$emit('modalMessage', message); + $scope.$emit('raiseError', true); if(vm.isPublic) { vm.isPublic = false; @@ -26,16 +45,6 @@ vm.isPublic = true; } - ToggleProjectPublicityService(vm.projectId, vm.isPublic) - .success(toggleProjectPublicitySuccess) - .error(toggleProjectPublicityFailed); - } - - function toggleProjectPublicitySuccess(data, status) { - console.log('Successful toggle project publicity.'); - } - - function toggleProjectPublicityFailed(e) { console.log('Failed to toggle project publicity:' + e); } } @@ -57,7 +66,11 @@ return directive; function link(scope, element, attr, ctrl) { - + scope.$watch('vm.isPublic', function(current, origin) { + if(current) { + ctrl.isPublic = current; + } + }); } } diff --git a/static/resources/js/components/replication/create-policy.directive.js b/static/resources/js/components/replication/create-policy.directive.js index 100273c..b1bc16a 100644 --- a/static/resources/js/components/replication/create-policy.directive.js +++ b/static/resources/js/components/replication/create-policy.directive.js @@ -171,6 +171,7 @@ vm.policy = policy; if(vm.targetEditable) { vm.policy.targetId = vm1.selection.id; + saveDestination(); saveOrUpdatePolicy(); } } @@ -366,6 +367,8 @@ ctrl.pingMessage = ''; ctrl.pingAvailable = true; + ctrl.saveTIP = false; + ctrl.pingTIP = false; ctrl.toggleErrorMessage = false; ctrl.errorMessages = []; diff --git a/static/resources/js/components/replication/list-replication.directive.html b/static/resources/js/components/replication/list-replication.directive.html index f748c88..bc5a845 100644 --- a/static/resources/js/components/replication/list-replication.directive.html +++ b/static/resources/js/components/replication/list-replication.directive.html @@ -41,8 +41,8 @@
- - + +
  diff --git a/static/resources/js/components/replication/list-replication.directive.js b/static/resources/js/components/replication/list-replication.directive.js index 0b38dfe..e8dbd8c 100644 --- a/static/resources/js/components/replication/list-replication.directive.js +++ b/static/resources/js/components/replication/list-replication.directive.js @@ -15,6 +15,7 @@ {'key': 'pending', 'value': $filter('tr')('pending')}, {'key': 'running', 'value': $filter('tr')('running')}, {'key': 'error' , 'value': $filter('tr')('error')}, + {'key': 'retrying', 'value': $filter('tr')('retrying')}, {'key': 'stopped', 'value': $filter('tr')('stopped')}, {'key': 'finished', 'value':$filter('tr')('finished')}, {'key': 'canceled', 'value': $filter('tr')('canceled')} @@ -28,7 +29,7 @@ var vm = this; vm.sectionHeight = {'min-height': '1200px'}; - + $scope.$on('$locationChangeSuccess', function() { vm.projectId = getParameterByName('project_id', $location.absUrl()); vm.retrievePolicy(); @@ -59,7 +60,7 @@ vm.searchJobTIP = false; vm.refreshJobTIP = false; - + function searchReplicationPolicy() { vm.retrievePolicy(); } @@ -102,9 +103,17 @@ function listReplicationJobSuccess(data, status) { vm.replicationJobs = data || []; + var alertInfo = { + 'show': false, + 'message': '' + }; angular.forEach(vm.replicationJobs, function(item) { for(var key in item) { - var value = item[key] + var value = item[key]; + if(key === 'status' && (value === 'error' || value === 'retrying')) { + alertInfo.show = true; + alertInfo.message = $filter('tr')('alert_job_contains_error'); + } switch(key) { case 'operation': case 'status': @@ -114,6 +123,8 @@ } } }); + + $scope.$emit('raiseAlert', alertInfo); vm.searchJobTIP = false; vm.refreshJobTIP = false; } diff --git a/static/resources/js/components/repository/list-repository.directive.js b/static/resources/js/components/repository/list-repository.directive.js index 2bd1aca..2abd5dc 100644 --- a/static/resources/js/components/repository/list-repository.directive.js +++ b/static/resources/js/components/repository/list-repository.directive.js @@ -17,7 +17,7 @@ vm.filterInput = ''; vm.toggleInProgress = []; - + var hashValue = $location.hash(); if(hashValue) { var slashIndex = hashValue.indexOf('/'); diff --git a/static/resources/js/components/repository/list-tag.directive.js b/static/resources/js/components/repository/list-tag.directive.js index 38b3abb..c797bd5 100644 --- a/static/resources/js/components/repository/list-tag.directive.js +++ b/static/resources/js/components/repository/list-tag.directive.js @@ -31,13 +31,13 @@ function retrieve() { ListTagService(vm.repoName) - .then(getTagComplete) - .catch(getTagFailed); + .success(getTagSuccess) + .error(getTagFailed); } - function getTagComplete(response) { + function getTagSuccess(data) { - vm.tags = response.data; + vm.tags = data || []; vm.tagCount[vm.repoName] = vm.tags.length; $scope.$emit('tags', vm.tags); @@ -48,11 +48,11 @@ }); } - function getTagFailed(response) { + function getTagFailed(data) { $scope.$emit('modalTitle', $filter('tr')('error')); $scope.$emit('modalMessage', $filter('tr')('failed_to_get_tag') + response); $scope.$emit('raiseError', true); - console.log('Failed to get tag:' + response); + console.log('Failed to get tag:' + data); } function deleteTag(e) { diff --git a/static/resources/js/components/system-management/create-destination.directive.html b/static/resources/js/components/system-management/create-destination.directive.html index a5f1f50..04f0765 100644 --- a/static/resources/js/components/system-management/create-destination.directive.html +++ b/static/resources/js/components/system-management/create-destination.directive.html @@ -49,7 +49,7 @@
- +
diff --git a/static/resources/js/components/system-management/create-destination.directive.js b/static/resources/js/components/system-management/create-destination.directive.js index d185f38..a1f39b4 100644 --- a/static/resources/js/components/system-management/create-destination.directive.js +++ b/static/resources/js/components/system-management/create-destination.directive.js @@ -21,7 +21,7 @@ vm.pingDestination = pingDestination; vm.editable = true; - vm.notAvailable = true; + vm.notAvailable = false; vm.pingAvailable = true; vm.pingMessage = ''; @@ -40,7 +40,6 @@ }); function addNew() { - vm.editable = true; vm.modalTitle = $filter('tr')('add_new_destination', []); vm0.name = ''; vm0.endpoint = ''; @@ -153,7 +152,7 @@ function pingDestinationFailed(data, status) { vm.pingTIP = false; - vm.pingMessage = $filter('tr')('failed_to_ping_target', []) + (data && data.length > 0 ? ':' + data : '.'); + vm.pingMessage = $filter('tr')('failed_to_ping_target', []) + (data && data.length > 0 ? ':' + data : ''); } } @@ -176,33 +175,34 @@ function link(scope, element, attrs, ctrl) { element.find('#createDestinationModal').on('show.bs.modal', function() { - - scope.form.$setPristine(); - scope.form.$setUntouched(); - - ctrl.notAvailble = true; - ctrl.pingAvailable = true; - ctrl.pingMessage = ''; - - ctrl.toggleErrorMessage = false; - ctrl.errorMessages = []; - - switch(ctrl.action) { - case 'ADD_NEW': - ctrl.addNew(); - break; - case 'EDIT': - ctrl.edit(ctrl.targetId); - break; - } - - scope.$watch('vm.errorMessages', function(current) { - if(current && current.length > 0) { - ctrl.toggleErrorMessage = true; + scope.$apply(function(){ + scope.form.$setPristine(); + scope.form.$setUntouched(); + + ctrl.notAvailble = false; + ctrl.pingAvailable = true; + ctrl.pingMessage = ''; + + ctrl.pingTIP = false; + ctrl.toggleErrorMessage = false; + ctrl.errorMessages = []; + + switch(ctrl.action) { + case 'ADD_NEW': + ctrl.addNew(); + break; + case 'EDIT': + ctrl.edit(ctrl.targetId); + break; } - }, true); - - scope.$apply(); + + scope.$watch('vm.errorMessages', function(current) { + if(current && current.length > 0) { + ctrl.toggleErrorMessage = true; + } + }, true); + + }); }); ctrl.save = save; diff --git a/static/resources/js/components/system-management/destination.directive.html b/static/resources/js/components/system-management/destination.directive.html index c811465..8dccd16 100644 --- a/static/resources/js/components/system-management/destination.directive.html +++ b/static/resources/js/components/system-management/destination.directive.html @@ -33,9 +33,9 @@ //r.endpoint// //r.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'// - +   - + diff --git a/static/resources/js/components/system-management/replication.directive.html b/static/resources/js/components/system-management/replication.directive.html index b687cb1..e55aeec 100644 --- a/static/resources/js/components/system-management/replication.directive.html +++ b/static/resources/js/components/system-management/replication.directive.html @@ -42,11 +42,11 @@
- - + +
  - +   diff --git a/static/resources/js/components/user/toggle-admin.directive.js b/static/resources/js/components/user/toggle-admin.directive.js index 999e796..d0eac16 100644 --- a/static/resources/js/components/user/toggle-admin.directive.js +++ b/static/resources/js/components/user/toggle-admin.directive.js @@ -34,6 +34,11 @@ $scope.$emit('modalTitle', $filter('tr')('error')); $scope.$emit('modalMessage', $filter('tr')('failed_to_toggle_admin')); $scope.$emit('raiseError', true); + if(vm.isAdmin) { + vm.isAdmin = false; + }else{ + vm.isAdmin = true; + } console.log('Failed to toggle admin:' + data); } } diff --git a/static/resources/js/harbor.config.js b/static/resources/js/harbor.config.js index 2196a1f..8e8c392 100644 --- a/static/resources/js/harbor.config.js +++ b/static/resources/js/harbor.config.js @@ -20,17 +20,36 @@ function RedirectInterceptorService($q, $window) { return { 'responseError': function(rejection) { - var pathname = $window.location.pathname; - var exclusion = ['/', '/search', '/reset_password', '/sign_up', '/forgot_password', '/repository']; + var url = rejection.config.url; + console.log('url:' + url); + var exclusion = [ + '/', + '/search', + '/reset_password', + '/sign_up', + '/forgot_password', + '/api/targets/ping', + '/api/users/current', + '/api/repositories', + /^\/api\/projects\/[0-9]+\/members\/current$/ + ]; var isExcluded = false; for(var i in exclusion) { - if(exclusion[i] === pathname) { - isExcluded = true; + switch(typeof(exclusion[i])) { + case 'string': + isExcluded = (exclusion[i] === url); + break; + case 'object': + isExcluded = exclusion[i].test(url); break; } - } - if(rejection.status === 401 && !isExcluded) { + if(isExcluded) { + break; + } + } + if(!isExcluded && rejection.status === 401) { $window.location.href = '/'; + return; } return $q.reject(rejection); } diff --git a/static/resources/js/harbor.module.js b/static/resources/js/harbor.module.js index 3b7e85a..d92272c 100644 --- a/static/resources/js/harbor.module.js +++ b/static/resources/js/harbor.module.js @@ -45,6 +45,7 @@ 'harbor.replication', 'harbor.system.management', 'harbor.loading.progress', - 'harbor.inline.help' + 'harbor.inline.help', + 'harbor.dismissable.alerts' ]); })(); \ No newline at end of file diff --git a/static/resources/js/layout/details/details.controller.js b/static/resources/js/layout/details/details.controller.js index 974ae16..c2d6c03 100644 --- a/static/resources/js/layout/details/details.controller.js +++ b/static/resources/js/layout/details/details.controller.js @@ -15,7 +15,6 @@ vm.isProjectMember = false; vm.togglePublicity = togglePublicity; - vm.target = 'repositories'; vm.sectionDefaultHeight = {'min-height': '579px'}; @@ -55,7 +54,7 @@ } }); - function togglePublicity(e) { + function togglePublicity(e) { vm.publicity = e.publicity; } } diff --git a/static/resources/js/services/i18n/locale_messages_en-US.js b/static/resources/js/services/i18n/locale_messages_en-US.js index b14060f..f72d492 100644 --- a/static/resources/js/services/i18n/locale_messages_en-US.js +++ b/static/resources/js/services/i18n/locale_messages_en-US.js @@ -79,6 +79,7 @@ var locale_messages = { 'new_project': 'New Project', 'save': 'Save', 'cancel': 'Cancel', + 'confirm': 'Confirm', 'items': 'items', 'add_member': 'Add Member', 'operation': 'Operation', @@ -153,7 +154,9 @@ var locale_messages = { 'status': 'Status', 'logs' : 'Logs', 'enabled': 'Enabled', + 'enable': 'Enable', 'disabled': 'Disabled', + 'disable': 'Disable', 'no_replication_policies_add_new': 'No replication policies, add new replication policy.', 'no_replication_policies': 'No replication policies.', 'no_replication_jobs': 'No replication jobs.', @@ -186,9 +189,9 @@ var locale_messages = { 'successful_added': 'Added new user successfully.', 'copyright': 'Copyright', 'all_rights_reserved': 'All Rights Reserved.', - 'pinging_target': 'Testing connection, please wait...', + 'pinging_target': 'Testing connection, please stand by...', 'successful_ping_target': 'Test connection successfully.', - 'failed_to_ping_target': 'Faild to connect target.', + 'failed_to_ping_target': 'Cannot connect to the target.', 'policy_already_exists': 'Policy alreay exists.', 'destination_already_exists': 'Destination already exists.', 'refresh': 'Refresh', @@ -205,6 +208,7 @@ var locale_messages = { 'finished': 'Finished', 'canceled': 'Canceled', 'stopped': 'Stopped', + 'retrying': 'Retrying', 'error': 'Error', 'failed_to_get_project_member': 'Failed to get current project member.', 'failed_to_delete_repo': 'Failed to delete repository. ', @@ -235,6 +239,8 @@ var locale_messages = { 'failed_to_delete_destination': 'Failed to delete destination.', 'failed_to_create_destination': 'Failed to create destination.', 'failed_to_update_destination': 'Failed to update destination.', + 'failed_to_toggle_publicity_insuffient_permissions': 'Failed to toggle project publicity, insuffient permissions.', + 'failed_to_toggle_publicity': 'Failed to toggle project publicity.', 'project_admin': 'Project Admin', 'developer': 'Developer', 'guest': 'Guest', @@ -243,6 +249,7 @@ var locale_messages = { 'Developer: Developer has read and write privileges for a project.
' + 'Guest: Guest has read-only privilege for a specified project.', 'inline_help_publicity_title': 'Publicity of Project', - 'inline_help_publicity': 'Setting the project as public.' - + 'inline_help_publicity': 'Setting the project as public.', + 'alert_job_contains_error': 'Found errors in the current replication jobs, please look into it.', + 'caution': 'Caution' }; \ No newline at end of file diff --git a/static/resources/js/services/i18n/locale_messages_zh-CN.js b/static/resources/js/services/i18n/locale_messages_zh-CN.js index 3d34db4..895d959 100644 --- a/static/resources/js/services/i18n/locale_messages_zh-CN.js +++ b/static/resources/js/services/i18n/locale_messages_zh-CN.js @@ -79,6 +79,7 @@ var locale_messages = { 'new_project': '新增项目', 'save': '保存', 'cancel': '取消', + 'confirm': '确认', 'items': '条记录', 'add_member': '新增成员', 'operation': '操作', @@ -150,8 +151,10 @@ var locale_messages = { 'actions': '操作', 'status': '状态', 'logs': '日志', - 'enabled': '启用', - 'disabled': '停用', + 'enabled': '已启用', + 'enable': '启用', + 'disabled': '已停用', + 'disable': '停用', 'no_replication_policies_add_new': '没有复制策略,请新增复制策略。', 'no_replication_policies': '没有复制策略。', 'no_replications': '没有复制策略。', @@ -160,7 +163,6 @@ var locale_messages = { 'name_is_required': '名称为必填项', 'name_is_too_long': '名称长度超出限制。(最长为20个字符)', 'description_is_too_long': '描述内容长度超出限制。(最长为20个字符)', - 'enable': '启用', 'general_setting': '一般设置', 'destination_setting': '目标设置', 'endpoint': '终端URL', @@ -199,12 +201,13 @@ var locale_messages = { 'edit': '修改', 'delete': '删除', 'all': '全部', - 'transfer': '传输', - 'pending': '等待', + 'transfer': '复制', + 'pending': '等待中', 'running': '进行中', 'finished': '已完成', - 'canceled': '取消', - 'stopped': '停止', + 'canceled': '已取消', + 'stopped': '已终止', + 'retrying': '重试中', 'error': '错误', 'failed_to_get_project_member': '无法获取当前项目成员。', 'failed_to_delete_repo': '无法删除镜像仓库。', @@ -235,6 +238,8 @@ var locale_messages = { 'failed_to_delete_destination': '删除目标失败。', 'failed_to_create_destination': '创建目标失败。', 'failed_to_update_destination': '修改目标失败。', + 'failed_to_toggle_publicity_insuffient_permissions': '切换项目公开失败,权限不足。', + 'failed_to_toggle_publicity': '切换项目公开失败。', 'project_admin': '项目管理员', 'developer': '开发人员', 'guest': '来宾用户', @@ -243,5 +248,7 @@ var locale_messages = { '开发人员: “开发人员” 拥有一个项目的读/写权限。
' + '来宾用户: “来宾用户”拥有特定项目的只读权限。', 'inline_help_publicity_title': '公开项目', - 'inline_help_publicity': '设置该项目为公开。' + 'inline_help_publicity': '设置该项目为公开。', + 'alert_job_contains_error': '当前复制任务中包含错误,请检查。', + 'caution': '注意' }; \ No newline at end of file diff --git a/views/repository.htm b/views/repository.htm index 7e5f45f..7aaa029 100644 --- a/views/repository.htm +++ b/views/repository.htm @@ -2,7 +2,7 @@
-
+
+
diff --git a/views/sections/header-include.htm b/views/sections/header-include.htm index 5f83064..a78d61c 100644 --- a/views/sections/header-include.htm +++ b/views/sections/header-include.htm @@ -240,4 +240,7 @@ - \ No newline at end of file + + + + \ No newline at end of file