\ No newline at end of file
diff --git a/static/resources/js/components/details/switch-pane-projects.directive.js b/static/resources/js/components/details/switch-pane-projects.directive.js
new file mode 100644
index 000000000..d0a8c994e
--- /dev/null
+++ b/static/resources/js/components/details/switch-pane-projects.directive.js
@@ -0,0 +1,64 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.details')
+ .directive('switchPaneProjects', switchPaneProjects);
+
+ SwitchPaneProjectsController.$inject = ['$scope'];
+
+ function SwitchPaneProjectsController($scope) {
+ var vm = this;
+
+ $scope.$watch('vm.selectedProject', function(current, origin) {
+ if(current){
+ vm.projectName = current.name;
+ vm.selectedProject = current;
+ }
+ });
+
+ vm.switchPane = switchPane;
+
+ function switchPane() {
+ if(vm.isOpen) {
+ vm.isOpen = false;
+ }else{
+ vm.isOpen = true;
+ }
+ }
+
+ }
+
+ function switchPaneProjects() {
+ var directive = {
+ restrict: 'E',
+ templateUrl: '/static/resources/js/components/details/switch-pane-projects.directive.html',
+ scope: {
+ 'isOpen': '=',
+ 'selectedProject': '='
+ },
+ controller: SwitchPaneProjectsController,
+ controllerAs: 'vm',
+ bindToController: true
+ };
+
+ return directive;
+
+ }
+
+})();
\ No newline at end of file
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 000000000..9c8c5dbbd
--- /dev/null
+++ b/static/resources/js/components/dismissable-alerts/dismissable-alerts.directive.html
@@ -0,0 +1,18 @@
+
+
+
+ // 'caution' | tr // //message//
+
\ 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 000000000..9b94a1641
--- /dev/null
+++ b/static/resources/js/components/dismissable-alerts/dismissable-alerts.directive.js
@@ -0,0 +1,48 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(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 000000000..cc1f2e2a7
--- /dev/null
+++ b/static/resources/js/components/dismissable-alerts/dismissable-alerts.module.js
@@ -0,0 +1,21 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular.module('harbor.dismissable.alerts', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/element-height/element-height.inspector.js b/static/resources/js/components/element-height/element-height.inspector.js
new file mode 100644
index 000000000..769f2f068
--- /dev/null
+++ b/static/resources/js/components/element-height/element-height.inspector.js
@@ -0,0 +1,62 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.element.height')
+ .directive('elementHeight', elementHeight);
+
+ function elementHeight($window) {
+ var directive = {
+ 'restrict': 'A',
+ 'link': link
+ };
+
+ return directive;
+
+ function link(scope, element, attrs) {
+
+ var w = angular.element($window);
+
+ scope.getDimension = function() {
+ return {'h' : w.height()};
+ };
+
+ if(!angular.isDefined(scope.subsHeight)) scope.subsHeight = 110;
+ if(!angular.isDefined(scope.subsSection)) scope.subsSection = 32;
+ if(!angular.isDefined(scope.subsSubPane)) scope.subsSubPane = 226;
+ if(!angular.isDefined(scope.subsTblBody)) scope.subsTblBody = 40;
+
+ scope.$watch(scope.getDimension, function(current) {
+ if(current) {
+ var h = current.h;
+ element.css({'height': (h - scope.subsHeight) + 'px'});
+ element.find('.section').css({'height': (h - scope.subsHeight - scope.subsSection) + 'px'});
+ element.find('.sub-pane').css({'height': (h - scope.subsHeight - scope.subsSubPane) + 'px'});
+ element.find('.tab-pane').css({'height': (h - scope.subsHeight - scope.subsSubPane - scope.subsSection -100) + 'px'});
+// var subPaneHeight = element.find('.sub-pane').height();
+// element.find('.table-body-container').css({'height': (subPaneHeight - scope.subsTblBody) + 'px'});
+ }
+ }, true);
+
+ w.on('pageshow, resize', function() {
+ scope.$apply();
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/element-height/element-height.module.js b/static/resources/js/components/element-height/element-height.module.js
new file mode 100644
index 000000000..14be71f1e
--- /dev/null
+++ b/static/resources/js/components/element-height/element-height.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.element.height', []);
+
+})();
\ No newline at end of file
diff --git a/views/segment/base-layout.tpl b/static/resources/js/components/inline-help/inline-help.directive.html
similarity index 76%
rename from views/segment/base-layout.tpl
rename to static/resources/js/components/inline-help/inline-help.directive.html
index e332907ba..e7b935299 100644
--- a/views/segment/base-layout.tpl
+++ b/static/resources/js/components/inline-help/inline-help.directive.html
@@ -12,17 +12,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-
-
- {{.HeaderInc}}
- {{.PageTitle}}
-
-
- {{.HeaderContent}}
- {{.BodyContent}}
- {{.FooterInc}}
- {{.ModalDialog}}
- {{.FootContent}}
-
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/inline-help/inline-help.directive.js b/static/resources/js/components/inline-help/inline-help.directive.js
new file mode 100644
index 000000000..cf2f30fe7
--- /dev/null
+++ b/static/resources/js/components/inline-help/inline-help.directive.js
@@ -0,0 +1,47 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.inline.help')
+ .directive('inlineHelp', inlineHelp);
+ function InlineHelpController() {
+ var vm = this;
+ }
+ function inlineHelp() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/inline-help/inline-help.directive.html',
+ 'scope': {
+ 'helpTitle': '@',
+ 'content': '@'
+ },
+ 'link': link,
+ 'controller': InlineHelpController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+ function link(scope, element, attr, ctrl) {
+ element.popover({
+ 'title': ctrl.helpTitle,
+ 'content': ctrl.content,
+ 'html': true
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/inline-help/inline-help.module.js b/static/resources/js/components/inline-help/inline-help.module.js
new file mode 100644
index 000000000..26311190e
--- /dev/null
+++ b/static/resources/js/components/inline-help/inline-help.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.inline.help', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/loading-progress/loading-progress.directive.js b/static/resources/js/components/loading-progress/loading-progress.directive.js
new file mode 100644
index 000000000..030f19edf
--- /dev/null
+++ b/static/resources/js/components/loading-progress/loading-progress.directive.js
@@ -0,0 +1,70 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.loading.progress')
+ .directive('loadingProgress', loadingProgress);
+
+ function loadingProgress() {
+ var directive = {
+ 'restrict': 'EA',
+ 'scope': {
+ 'toggleInProgress': '=',
+ 'hideTarget': '@'
+ },
+ 'link': link
+ };
+
+ return directive;
+
+ function link(scope, element, attrs) {
+ var spinner = $('');
+
+ function convertToBoolean(val) {
+ return val === 'true' ? true : false;
+ }
+
+ var hideTarget = convertToBoolean(scope.hideTarget);
+
+ console.log('loading-progress, toggleInProgress:' + scope.toggleInProgress + ', hideTarget:' + hideTarget);
+
+ var pristine = element.html();
+
+ scope.$watch('toggleInProgress', function(current) {
+ if(scope.toggleInProgress) {
+ element.attr('disabled', 'disabled');
+ if(hideTarget) {
+ element.html(spinner);
+ }else{
+ spinner = spinner.css({'margin-left': '5px'});
+ element.append(spinner);
+ }
+ }else{
+ if(hideTarget) {
+ element.html(pristine);
+ }else{
+ element.find('.loading-progress').remove();
+ }
+ element.removeAttr('disabled');
+ }
+ });
+
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/loading-progress/loading-progress.module.js b/static/resources/js/components/loading-progress/loading-progress.module.js
new file mode 100644
index 000000000..076da55c5
--- /dev/null
+++ b/static/resources/js/components/loading-progress/loading-progress.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.loading.progress', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/log/advanced-search.directive.html b/static/resources/js/components/log/advanced-search.directive.html
new file mode 100644
index 000000000..022dc9614
--- /dev/null
+++ b/static/resources/js/components/log/advanced-search.directive.html
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/log/advanced-search.directive.js b/static/resources/js/components/log/advanced-search.directive.js
new file mode 100644
index 000000000..31a858deb
--- /dev/null
+++ b/static/resources/js/components/log/advanced-search.directive.js
@@ -0,0 +1,165 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.log')
+ .directive('advancedSearch', advancedSearch);
+
+ AdvancedSearchController.$inject = ['$scope', 'ListLogService'];
+
+ function AdvancedSearchController($scope, ListLogService) {
+ var vm = this;
+
+ vm.checkOperation = checkOperation;
+ vm.close = close;
+
+ vm.opAll = true;
+
+ vm.doSearch = doSearch;
+
+ $scope.$watch('vm.op', function(current) {
+ if(current && vm.op[0] === 'all') {
+ vm.opCreate = true;
+ vm.opPull = true;
+ vm.opPush = true;
+ vm.opDelete = true;
+ vm.opOthers = true;
+ }
+ }, true);
+
+ $scope.$watch('vm.fromDate', function(current) {
+ if(current) {
+ vm.fromDate = current;
+ }
+ });
+
+ $scope.$watch('vm.toDate', function(current) {
+ if(current) {
+ vm.toDate = current;
+ }
+ });
+
+
+ vm.opCreate = true;
+ vm.opPull = true;
+ vm.opPush = true;
+ vm.opDelete = true;
+ vm.opOthers = true;
+ vm.others = '';
+
+ vm.op = [];
+ vm.op.push('all');
+ function checkOperation(e) {
+ if(e.checked === 'all') {
+ vm.opCreate = vm.opAll;
+ vm.opPull = vm.opAll;
+ vm.opPush = vm.opAll;
+ vm.opDelete = vm.opAll;
+ vm.opOthers = vm.opAll;
+ }else {
+ vm.opAll = false;
+ }
+
+ vm.op = [];
+
+ if(vm.opCreate) {
+ vm.op.push('create');
+ }
+ if(vm.opPull) {
+ vm.op.push('pull');
+ }
+ if(vm.opPush) {
+ vm.op.push('push');
+ }
+ if(vm.opDelete) {
+ vm.op.push('delete');
+ }
+ if(vm.opOthers && $.trim(vm.others) !== '') {
+ vm.op.push($.trim(vm.others));
+ }
+ }
+
+ vm.pickUp = pickUp;
+
+ function pickUp(e) {
+ switch(e.key){
+ case 'fromDate':
+ vm.fromDate = e.value;
+ break;
+ case 'toDate':
+ vm.toDate = e.value;
+ break;
+ }
+ $scope.$apply();
+ }
+
+ function close() {
+ vm.op = [];
+ vm.op.push('all');
+ vm.fromDate = '';
+ vm.toDate = '';
+ vm.others = '';
+ vm.isOpen = false;
+ }
+
+ function doSearch (e){
+ if(vm.opOthers && $.trim(vm.others) !== '') {
+ e.op.push(vm.others);
+ }
+ vm.search(e);
+ }
+ }
+
+ function advancedSearch(I18nService) {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/log/advanced-search.directive.html',
+ 'scope': {
+ 'isOpen': '=',
+ 'op': '=',
+ 'opOthers': '=',
+ 'others': '=',
+ 'fromDate': '=',
+ 'toDate': '=',
+ 'search': '&'
+ },
+ 'link': link,
+ 'controller': AdvancedSearchController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+ element.find('.datetimepicker').datetimepicker({
+ locale: I18nService().getCurrentLanguage(),
+ ignoreReadonly: true,
+ format: 'L',
+ showClear: true
+ });
+ element.find('#fromDatePicker').on('blur', function(){
+ ctrl.pickUp({'key': 'fromDate', 'value': $(this).val()});
+ });
+ element.find('#toDatePicker').on('blur', function(){
+ ctrl.pickUp({'key': 'toDate', 'value': $(this).val()});
+ });
+
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/log/list-log.directive.html b/static/resources/js/components/log/list-log.directive.html
new file mode 100644
index 000000000..912498dd4
--- /dev/null
+++ b/static/resources/js/components/log/list-log.directive.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// 'username' | tr //
+
// 'repository_name' | tr //
+
// 'operation' | tr //
+
// 'timestamp' | tr //
+
+
+
+
+
+
+
+
//log.username//
//log.repo_name//
//log.operation//
//log.op_time | dateL : 'YYYY-MM-DD HH:mm:ss'//
+
+
+
+
+
+
+
+
diff --git a/static/resources/js/components/log/list-log.directive.js b/static/resources/js/components/log/list-log.directive.js
new file mode 100644
index 000000000..5c4868125
--- /dev/null
+++ b/static/resources/js/components/log/list-log.directive.js
@@ -0,0 +1,163 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.log')
+ .directive('listLog', listLog);
+
+ ListLogController.$inject = ['$scope','ListLogService', 'getParameterByName', '$location', '$filter', 'trFilter'];
+
+ function ListLogController($scope, ListLogService, getParameterByName, $location, $filter, trFilter) {
+
+ $scope.subsTabPane = 30;
+
+ var vm = this;
+
+ vm.sectionHeight = {'min-height': '579px'};
+
+ vm.isOpen = false;
+
+ vm.beginTimestamp = 0;
+ vm.endTimestamp = 0;
+ vm.keywords = '';
+ vm.username = '';
+
+ vm.op = [];
+ vm.opOthers = true;
+
+ vm.search = search;
+ vm.showAdvancedSearch = showAdvancedSearch;
+
+ vm.projectId = getParameterByName('project_id', $location.absUrl());
+ vm.queryParams = {
+ 'beginTimestamp' : vm.beginTimestamp,
+ 'endTimestamp' : vm.endTimestamp,
+ 'keywords' : vm.keywords,
+ '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,
+ 'endTimestamp' : vm.endTimestamp,
+ 'keywords' : vm.keywords,
+ 'projectId': vm.projectId,
+ 'username' : vm.username
+ };
+ vm.username = '';
+ retrieve(vm.queryParams);
+ });
+
+ function search(e) {
+
+ if(e.op[0] === 'all') {
+ e.op = ['create', 'pull', 'push', 'delete'];
+ }
+ if(vm.opOthers && $.trim(vm.others) !== '') {
+ e.op.push(vm.others);
+ }
+
+ vm.queryParams.keywords = e.op.join('/');
+ vm.queryParams.username = e.username;
+
+ vm.queryParams.beginTimestamp = toUTCSeconds(vm.fromDate, 0, 0, 0);
+ vm.queryParams.endTimestamp = toUTCSeconds(vm.toDate, 23, 59, 59);
+
+ retrieve(vm.queryParams);
+ }
+
+ function showAdvancedSearch() {
+ if(vm.isOpen){
+ vm.isOpen = false;
+ }else{
+ vm.isOpen = true;
+ }
+ }
+
+ function retrieve(queryParams) {
+ ListLogService(queryParams)
+ .then(listLogComplete)
+ .catch(listLogFailed);
+ }
+
+ function listLogComplete(response) {
+ vm.logs = response.data;
+
+ vm.queryParams = {
+ 'beginTimestamp' : 0,
+ 'endTimestamp' : 0,
+ 'keywords' : '',
+ 'projectId': vm.projectId,
+ 'username' : ''
+ };
+ vm.op = ['all'];
+ vm.fromDate = '';
+ vm.toDate = '';
+ vm.others = '';
+ vm.opOthers = true;
+ vm.isOpen = false;
+ }
+ function listLogFailed(response){
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_get_log') + response);
+ $scope.$emit('raiseError', true);
+ console.log('Failed to get log:' + response);
+ }
+
+ function toUTCSeconds(date, hour, min, sec) {
+ if(!angular.isDefined(date) || date === '') {
+ return 0;
+ }
+
+ var t = new Date(date);
+ t.setHours(hour);
+ t.setMinutes(min);
+ t.setSeconds(sec);
+
+ return t.getTime() / 1000;
+ }
+
+ }
+
+ function listLog() {
+ var directive = {
+ restrict: 'E',
+ templateUrl: '/static/resources/js/components/log/list-log.directive.html',
+ scope: {
+ 'sectionHeight': '=',
+ 'target': '=',
+ 'publicity': '='
+ },
+ controller: ListLogController,
+ controllerAs: 'vm',
+ bindToController: true
+ };
+
+ return directive;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/log/log.config.js b/static/resources/js/components/log/log.config.js
new file mode 100644
index 000000000..1f5e1c73d
--- /dev/null
+++ b/static/resources/js/components/log/log.config.js
@@ -0,0 +1,23 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.log');
+
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/log/log.module.js b/static/resources/js/components/log/log.module.js
new file mode 100644
index 000000000..94bf397ae
--- /dev/null
+++ b/static/resources/js/components/log/log.module.js
@@ -0,0 +1,24 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.log', [
+ 'harbor.services.log'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/modal-dialog/modal-dialog.directive.html b/static/resources/js/components/modal-dialog/modal-dialog.directive.html
new file mode 100644
index 000000000..257d6b09e
--- /dev/null
+++ b/static/resources/js/components/modal-dialog/modal-dialog.directive.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
//vm.modalTitle//
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/modal-dialog/modal-dialog.directive.js b/static/resources/js/components/modal-dialog/modal-dialog.directive.js
new file mode 100644
index 000000000..dc1722049
--- /dev/null
+++ b/static/resources/js/components/modal-dialog/modal-dialog.directive.js
@@ -0,0 +1,90 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.modal.dialog')
+ .directive('modalDialog', modalDialog);
+
+ ModalDialogController.$inject = ['$scope'];
+
+ function ModalDialogController($scope) {
+ var vm = this;
+
+ }
+
+ function modalDialog() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/modal-dialog/modal-dialog.directive.html',
+ 'link': link,
+ 'scope': {
+ 'contentType': '@',
+ 'modalTitle': '@',
+ 'modalMessage': '@',
+ 'action': '&',
+ 'confirmOnly': '='
+ },
+ 'controller': ModalDialogController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+
+ scope.$watch('contentType', function(current) {
+ if(current) {
+ ctrl.contentType = current;
+ }
+ })
+ scope.$watch('confirmOnly', function(current) {
+ if(current) {
+ ctrl.confirmOnly = current;
+ }
+ })
+
+ scope.$watch('vm.modalMessage', function(current) {
+ if(current) {
+ switch(ctrl.contentType) {
+ case 'text/html':
+ element.find('.modal-body').html(current); break;
+ case 'text/plain':
+ element.find('.modal-body').text(current); break;
+ default:
+ element.find('.modal-body').text(current); break;
+ }
+ }
+ });
+
+ scope.$on('showDialog', function(e, val) {
+ if(val) {
+ element.find('#myModal').modal('show');
+ }else{
+ element.find('#myModal').modal('hide');
+ }
+ });
+
+ element.find('#btnOk').on('click', clickHandler);
+
+ function clickHandler(e) {
+ ctrl.action();
+ }
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/modal-dialog/modal-dialog.module.js b/static/resources/js/components/modal-dialog/modal-dialog.module.js
new file mode 100644
index 000000000..2ac2ba083
--- /dev/null
+++ b/static/resources/js/components/modal-dialog/modal-dialog.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.modal.dialog', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/optional-menu/optional-menu.directive.js b/static/resources/js/components/optional-menu/optional-menu.directive.js
new file mode 100644
index 000000000..4852b7eeb
--- /dev/null
+++ b/static/resources/js/components/optional-menu/optional-menu.directive.js
@@ -0,0 +1,71 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.optional.menu')
+ .directive('optionalMenu', optionalMenu);
+
+ OptionalMenuController.$inject = ['$window', 'I18nService', 'LogOutService', 'currentUser', '$timeout'];
+
+ function OptionalMenuController($window, I18nService, LogOutService, currentUser, $timeout) {
+ var vm = this;
+
+ vm.currentLanguage = I18nService().getCurrentLanguage();
+ vm.languageName = I18nService().getLanguageName(vm.currentLanguage);
+
+ I18nService().setCurrentLanguage(vm.currentLanguage);
+
+ console.log('current language:' + vm.languageName);
+
+ vm.supportLanguages = I18nService().getSupportLanguages();
+ vm.user = currentUser.get();
+ vm.setLanguage = setLanguage;
+ vm.logOut = logOut;
+
+ function setLanguage(language) {
+ I18nService().setCurrentLanguage(language);
+ var hash = $window.location.hash;
+ $window.location.href = '/language?lang=' + language + '&hash=' + encodeURIComponent(hash);
+ }
+ function logOut() {
+ LogOutService()
+ .success(logOutSuccess)
+ .error(logOutFailed);
+ }
+ function logOutSuccess(data, status) {
+ currentUser.unset();
+ $window.location.href= '/';
+ }
+ function logOutFailed(data, status) {
+ console.log('Failed to log out:' + data);
+ }
+ }
+
+ function optionalMenu() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/optional_menu?timestamp=' + new Date().getTime(),
+ 'scope': true,
+ 'controller': OptionalMenuController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/optional-menu/optional-menu.module.js b/static/resources/js/components/optional-menu/optional-menu.module.js
new file mode 100644
index 000000000..71cde6b35
--- /dev/null
+++ b/static/resources/js/components/optional-menu/optional-menu.module.js
@@ -0,0 +1,25 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.optional.menu', [
+ 'harbor.services.user',
+ 'harbor.services.i18n'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/project-member/add-project-member.directive.html b/static/resources/js/components/project-member/add-project-member.directive.html
new file mode 100644
index 000000000..2519fac55
--- /dev/null
+++ b/static/resources/js/components/project-member/add-project-member.directive.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/project-member/add-project-member.directive.js b/static/resources/js/components/project-member/add-project-member.directive.js
new file mode 100644
index 000000000..1f6251492
--- /dev/null
+++ b/static/resources/js/components/project-member/add-project-member.directive.js
@@ -0,0 +1,115 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.project.member')
+ .directive('addProjectMember', addProjectMember);
+
+ AddProjectMemberController.$inject = ['$scope', 'roles', 'AddProjectMemberService'];
+
+ function AddProjectMemberController($scope, roles, AddProjectMemberService) {
+ var vm = this;
+
+ $scope.pm = {};
+
+ var pm = $scope.pm;
+
+ vm.roles = roles();
+ vm.optRole = 1;
+
+ vm.save = save;
+ vm.cancel = cancel;
+ vm.reset = reset;
+
+ vm.hasError = false;
+ vm.errorMessage = '';
+
+ function save(pm) {
+ if(pm && angular.isDefined(pm.username)) {
+ AddProjectMemberService(vm.projectId, vm.optRole, pm.username)
+ .success(addProjectMemberComplete)
+ .error(addProjectMemberFailed);
+ }
+ }
+
+ function cancel(form) {
+
+ form.$setPristine();
+ form.$setUntouched();
+
+ vm.isOpen = false;
+ pm.username = '';
+ vm.optRole = 1;
+
+ vm.hasError = false;
+ vm.errorMessage = '';
+ }
+
+ function addProjectMemberComplete(data, status, header) {
+ console.log('addProjectMemberComplete: status:' + status + ', data:' + data);
+ vm.reload();
+ vm.isOpen = false;
+ }
+
+ function addProjectMemberFailed(data, status, headers) {
+ if(status === 403) {
+ vm.hasError = true;
+ vm.errorMessage = 'failed_to_add_member';
+ }
+ if(status === 409 && pm.username != '') {
+ vm.hasError = true;
+ vm.errorMessage = 'username_already_exist';
+ }
+ if(status === 404) {
+ vm.hasError = true;
+ vm.errorMessage = 'username_does_not_exist';
+ }
+ console.log('addProjectMemberFailed: status:' + status + ', data:' + data);
+ }
+
+ function reset() {
+ vm.hasError = false;
+ vm.errorMessage = '';
+ }
+
+ }
+
+ function addProjectMember() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/project-member/add-project-member.directive.html',
+ 'scope': {
+ 'projectId': '@',
+ 'isOpen': '=',
+ 'reload': '&'
+ },
+ 'link': link,
+ 'controller': AddProjectMemberController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+ scope.form.$setPristine();
+ scope.form.$setUntouched();
+ }
+ }
+
+})();
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
new file mode 100644
index 000000000..4f83d1a13
--- /dev/null
+++ b/static/resources/js/components/project-member/edit-project-member.directive.html
@@ -0,0 +1,25 @@
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/project-member/edit-project-member.directive.js b/static/resources/js/components/project-member/edit-project-member.directive.js
new file mode 100644
index 000000000..f1e73b48e
--- /dev/null
+++ b/static/resources/js/components/project-member/edit-project-member.directive.js
@@ -0,0 +1,99 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.project.member')
+ .directive('editProjectMember', editProjectMember);
+
+ EditProjectMemberController.$inject = ['$scope', 'roles', 'getRole','EditProjectMemberService', '$filter', 'trFilter'];
+
+ function EditProjectMemberController($scope, roles, getRole, EditProjectMemberService, $filter, trFilter) {
+ var vm = this;
+
+ vm.roles = roles();
+ vm.editMode = false;
+ vm.lastRoleName = vm.roleName;
+
+ $scope.$watch('vm.roleName', function(current, origin) {
+ if(current) {
+ vm.currentRole = getRole({'key': 'roleName', 'value': current});
+ vm.roleId = vm.currentRole.id;
+ }
+ });
+
+ vm.updateProjectMember = updateProjectMember;
+ vm.deleteProjectMember = deleteProjectMember;
+ vm.cancelUpdate = cancelUpdate;
+
+ function updateProjectMember(e) {
+ if(vm.editMode) {
+ console.log('update project member, roleId:' + e.roleId);
+ EditProjectMemberService(e.projectId, e.userId, e.roleId)
+ .success(editProjectMemberComplete)
+ .error(editProjectMemberFailed);
+ }else {
+ vm.editMode = true;
+ }
+ }
+
+ function deleteProjectMember() {
+ vm.delete();
+ }
+
+ function editProjectMemberComplete(data, status, headers) {
+ console.log('edit project member complete: ' + status);
+ vm.lastRoleName = vm.roleName;
+ vm.editMode = false;
+ vm.reload();
+ }
+
+ function editProjectMemberFailed(e) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_change_member'));
+ $scope.$emit('raiseError', true);
+ console.log('Failed to edit project member:' + e);
+ }
+
+ function cancelUpdate() {
+ vm.editMode = false;
+ vm.roleName = vm.lastRoleName;
+ }
+
+ }
+
+ function editProjectMember() {
+ var directive = {
+ 'restrict': 'A',
+ 'templateUrl': '/static/resources/js/components/project-member/edit-project-member.directive.html',
+ 'scope': {
+ 'username': '=',
+ 'userId': '=',
+ 'currentUserId': '=',
+ 'roleName': '=',
+ 'projectId': '=',
+ 'delete': '&',
+ 'reload': '&'
+ },
+ 'controller': EditProjectMemberController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/project-member/list-project-member.directive.html b/static/resources/js/components/project-member/list-project-member.directive.html
new file mode 100644
index 000000000..7e2654168
--- /dev/null
+++ b/static/resources/js/components/project-member/list-project-member.directive.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// 'username' | tr //
// 'role' | tr //
// 'operation' | tr //
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
new file mode 100644
index 000000000..cdcc28607
--- /dev/null
+++ b/static/resources/js/components/project-member/list-project-member.directive.js
@@ -0,0 +1,117 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.project.member')
+ .directive('listProjectMember', listProjectMember);
+
+ ListProjectMemberController.$inject = ['$scope', 'ListProjectMemberService', 'DeleteProjectMemberService', 'getParameterByName', '$location', 'currentUser', '$filter', 'trFilter', '$window'];
+
+ function ListProjectMemberController($scope, ListProjectMemberService, DeleteProjectMemberService, getParameterByName, $location, currentUser, $filter, trFilter, $window) {
+
+ $scope.subsTabPane = 30;
+
+ var vm = this;
+
+ vm.sectionHeight = {'min-height': '579px'};
+
+ vm.isOpen = false;
+ vm.search = search;
+ vm.addProjectMember = addProjectMember;
+ vm.deleteProjectMember = deleteProjectMember;
+ vm.retrieve = retrieve;
+ vm.username = '';
+
+ vm.projectId = getParameterByName('project_id', $location.absUrl());
+ vm.retrieve();
+
+ $scope.$on('$locationChangeSuccess', function() {
+ vm.projectId = getParameterByName('project_id', $location.absUrl());
+ vm.username = '';
+ vm.retrieve();
+ });
+
+ function search(e) {
+ vm.projectId = e.projectId;
+ vm.username = e.username;
+ retrieve();
+ }
+
+ function addProjectMember() {
+ if(vm.isOpen) {
+ vm.isOpen = false;
+ }else{
+ vm.isOpen = true;
+ }
+ }
+
+ function deleteProjectMember(e) {
+ DeleteProjectMemberService(e.projectId, e.userId)
+ .success(deleteProjectMemberSuccess)
+ .error(deleteProjectMemberFailed);
+ }
+
+ function deleteProjectMemberSuccess(data, status) {
+ console.log('Successful delete project member.');
+ vm.retrieve();
+ }
+
+ function deleteProjectMemberFailed(e) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_delete_member'));
+ $scope.$emit('raiseError', true);
+ console.log('Failed to edit project member:' + e);
+ }
+
+ function retrieve() {
+ ListProjectMemberService(vm.projectId, {'username': vm.username})
+ .then(getProjectMemberComplete)
+ .catch(getProjectMemberFailed);
+ }
+
+ function getProjectMemberComplete(response) {
+ vm.user = currentUser.get();
+ vm.projectMembers = response.data || [];
+ }
+
+ function getProjectMemberFailed(response) {
+ console.log('Failed to get project members:' + response);
+ vm.projectMembers = [];
+ vm.target = 'repositories';
+ $location.url('repositories').search('project_id', vm.projectId);
+ }
+
+ }
+
+ function listProjectMember() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/project-member/list-project-member.directive.html',
+ 'scope': {
+ 'sectionHeight': '=',
+ 'target': '='
+ },
+ 'controller': ListProjectMemberController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+
+ return directive;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/project-member/project-member.config.js b/static/resources/js/components/project-member/project-member.config.js
new file mode 100644
index 000000000..a63acc2ad
--- /dev/null
+++ b/static/resources/js/components/project-member/project-member.config.js
@@ -0,0 +1,48 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.project.member')
+ .constant('roles', roles)
+ .factory('getRole', getRole);
+
+ function roles() {
+ return [
+ {'id': '1', 'name': 'project_admin', 'roleName': 'projectAdmin'},
+ {'id': '2', 'name': 'developer', 'roleName': 'developer'},
+ {'id': '3', 'name': 'guest', 'roleName': 'guest'}
+ ];
+ }
+
+ getRole.$inject = ['roles', '$filter', 'trFilter'];
+
+ function getRole(roles, $filter, trFilter) {
+ var r = roles();
+ return get;
+ function get(query) {
+
+ for(var i = 0; i < r.length; i++) {
+ var role = r[i];
+ if(query.key === 'roleName' && role.roleName === query.value
+ || query.key === 'roleId' && role.id === String(query.value)) {
+ return role;
+ }
+ }
+ }
+ }
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/project-member/project-member.module.js b/static/resources/js/components/project-member/project-member.module.js
new file mode 100644
index 000000000..45c26aab3
--- /dev/null
+++ b/static/resources/js/components/project-member/project-member.module.js
@@ -0,0 +1,25 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.project.member', [
+ 'harbor.services.project.member',
+ 'harbor.services.user'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/project-member/switch-role.directive.html b/static/resources/js/components/project-member/switch-role.directive.html
new file mode 100644
index 000000000..dc3c8cef5
--- /dev/null
+++ b/static/resources/js/components/project-member/switch-role.directive.html
@@ -0,0 +1,19 @@
+
+
+ //vm.currentRole.name | tr//
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/project-member/switch-role.directive.js b/static/resources/js/components/project-member/switch-role.directive.js
new file mode 100644
index 000000000..6f9d0a352
--- /dev/null
+++ b/static/resources/js/components/project-member/switch-role.directive.js
@@ -0,0 +1,60 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.project.member')
+ .directive('switchRole', switchRole);
+
+ SwitchRoleController.$inject = ['getRole', '$scope'];
+
+ function SwitchRoleController(getRole, $scope) {
+ var vm = this;
+
+ $scope.$watch('vm.roleName', function(current,origin) {
+ if(current) {
+ vm.currentRole = getRole({'key': 'roleName', 'value': current});
+ }
+ });
+
+ vm.selectRole = selectRole;
+
+ function selectRole(role) {
+ vm.currentRole = getRole({'key': 'roleName', 'value': role.roleName});
+ vm.roleName = role.roleName;
+ }
+
+ }
+
+ function switchRole() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/project-member/switch-role.directive.html',
+ 'scope': {
+ 'roles': '=',
+ 'editMode': '=',
+ 'userId': '=',
+ 'roleName': '='
+ },
+ 'controller' : SwitchRoleController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/project/add-project.directive.html b/static/resources/js/components/project/add-project.directive.html
new file mode 100644
index 000000000..588da0a1c
--- /dev/null
+++ b/static/resources/js/components/project/add-project.directive.html
@@ -0,0 +1,42 @@
+
+
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/project/add-project.directive.js b/static/resources/js/components/project/add-project.directive.js
new file mode 100644
index 000000000..226eff3ad
--- /dev/null
+++ b/static/resources/js/components/project/add-project.directive.js
@@ -0,0 +1,109 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.project')
+ .directive('addProject', addProject);
+
+ AddProjectController.$inject = ['AddProjectService', '$scope'];
+
+ function AddProjectController(AddProjectService, $scope) {
+ var vm = this;
+
+ $scope.p = {};
+ var vm0 = $scope.p;
+ vm0.projectName = '';
+ vm.isPublic = false;
+
+ vm.addProject = addProject;
+ vm.cancel = cancel;
+
+ vm.reset = reset;
+
+ vm.hasError = false;
+ vm.errorMessage = '';
+
+ function addProject(p) {
+ if(p && angular.isDefined(p.projectName)) {
+ AddProjectService(p.projectName, vm.isPublic)
+ .success(addProjectSuccess)
+ .error(addProjectFailed);
+ }
+ }
+
+ function addProjectSuccess(data, status) {
+ $scope.$emit('addedSuccess', true);
+ vm.hasError = false;
+ vm.errorMessage = '';
+ vm.isOpen = false;
+ }
+
+ function addProjectFailed(data, status) {
+ vm.hasError = true;
+ if(status === 400 && vm0.projectName!= '' && vm0.projectName.length < 4) {
+ vm.errorMessage = 'project_name_is_too_short';
+ }
+ if(status === 400 && vm0.projectName.length > 30) {
+ vm.errorMessage = 'project_name_is_too_long';
+ }
+ if(status === 409 && vm0.projectName != '') {
+ vm.errorMessage = 'project_already_exist';
+ }
+ console.log('Failed to add project:' + status);
+ }
+
+ function cancel(form){
+ if(form) {
+ form.$setPristine();
+ form.$setUntouched();
+ }
+ vm.isOpen = false;
+ vm0.projectName = '';
+ vm.isPublic = false;
+
+ vm.hasError = false; vm.close = close;
+ vm.errorMessage = '';
+ }
+
+ function reset() {
+ vm.hasError = false;
+ vm.errorMessage = '';
+ }
+ }
+
+ function addProject() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/project/add-project.directive.html',
+ 'controller': AddProjectController,
+ 'scope' : {
+ 'isOpen': '='
+ },
+ 'link': link,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+ scope.form.$setPristine();
+ scope.form.$setUntouched();
+ }
+ }
+
+})();
diff --git a/static/resources/js/components/project/project.module.js b/static/resources/js/components/project/project.module.js
new file mode 100644
index 000000000..dd87d77b4
--- /dev/null
+++ b/static/resources/js/components/project/project.module.js
@@ -0,0 +1,23 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.project', [
+ 'harbor.services.project',
+ 'harbor.services.user'
+ ]);
+})();
\ No newline at end of file
diff --git a/views/segment/foot-content.tpl b/static/resources/js/components/project/publicity-button.directive.html
similarity index 70%
rename from views/segment/foot-content.tpl
rename to static/resources/js/components/project/publicity-button.directive.html
index 0bfa230a3..62460d3db 100644
--- a/views/segment/foot-content.tpl
+++ b/static/resources/js/components/project/publicity-button.directive.html
@@ -12,12 +12,5 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/project/publicity-button.directive.js b/static/resources/js/components/project/publicity-button.directive.js
new file mode 100644
index 000000000..2bb5d3b5d
--- /dev/null
+++ b/static/resources/js/components/project/publicity-button.directive.js
@@ -0,0 +1,91 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.project')
+ .directive('publicityButton', publicityButton);
+
+ PublicityButtonController.$inject = ['$scope', 'ToggleProjectPublicityService', '$filter', 'trFilter'];
+
+ function PublicityButtonController($scope, ToggleProjectPublicityService, $filter, trFilter) {
+ var vm = this;
+ vm.toggle = toggle;
+
+ function toggle() {
+ if(vm.isPublic) {
+ vm.isPublic = false;
+ }else{
+ 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, 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;
+ }else{
+ vm.isPublic = true;
+ }
+
+ console.log('Failed to toggle project publicity:' + e);
+ }
+ }
+
+ function publicityButton() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/project/publicity-button.directive.html',
+ 'scope': {
+ 'isPublic': '=',
+ 'owned': '=',
+ 'projectId': '='
+ },
+ 'link': link,
+ 'controller': PublicityButtonController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+
+ function link(scope, element, attr, ctrl) {
+ scope.$watch('vm.isPublic', function(current, origin) {
+ if(current) {
+ ctrl.isPublic = current;
+ }
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/replication/create-policy.directive.html b/static/resources/js/components/replication/create-policy.directive.html
new file mode 100644
index 000000000..73ba59753
--- /dev/null
+++ b/static/resources/js/components/replication/create-policy.directive.html
@@ -0,0 +1,120 @@
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/repository/list-repository.directive.js b/static/resources/js/components/repository/list-repository.directive.js
new file mode 100644
index 000000000..0c509777d
--- /dev/null
+++ b/static/resources/js/components/repository/list-repository.directive.js
@@ -0,0 +1,183 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.repository')
+ .directive('listRepository', listRepository);
+
+ ListRepositoryController.$inject = ['$scope', 'ListRepositoryService', 'DeleteRepositoryService', '$filter', 'trFilter', '$location', 'getParameterByName'];
+
+ function ListRepositoryController($scope, ListRepositoryService, DeleteRepositoryService, $filter, trFilter, $location, getParameterByName) {
+
+ $scope.subsTabPane = 30;
+
+ var vm = this;
+
+ vm.sectionHeight = {'min-height': '579px'};
+
+ vm.filterInput = '';
+ vm.toggleInProgress = [];
+
+ var hashValue = $location.hash();
+ if(hashValue) {
+ var slashIndex = hashValue.indexOf('/');
+ if(slashIndex >=0) {
+ vm.filterInput = hashValue.substring(slashIndex + 1);
+ }else{
+ vm.filterInput = hashValue;
+ }
+ }
+
+ vm.retrieve = retrieve;
+ vm.tagCount = {};
+
+ vm.projectId = getParameterByName('project_id', $location.absUrl());
+ vm.retrieve();
+
+ $scope.$on('$locationChangeSuccess', function() {
+ vm.projectId = getParameterByName('project_id', $location.absUrl());
+ vm.filterInput = '';
+ vm.retrieve();
+ });
+
+
+ $scope.$watch('vm.repositories', function(current) {
+ if(current) {
+ vm.repositories = current || [];
+ }
+ });
+
+ $scope.$on('repoName', function(e, val) {
+ vm.repoName = val;
+ });
+
+ $scope.$on('tag', function(e, val){
+ vm.tag = val;
+ });
+
+ $scope.$on('tagCount', function(e, val) {
+ vm.tagCount = val;
+ });
+
+ $scope.$on('tags', function(e, val) {
+ vm.tags = val;
+ });
+
+ vm.deleteByRepo = deleteByRepo;
+ vm.deleteByTag = deleteByTag;
+ vm.deleteImage = deleteImage;
+
+ function retrieve(){
+ ListRepositoryService(vm.projectId, vm.filterInput)
+ .success(getRepositoryComplete)
+ .error(getRepositoryFailed);
+ }
+
+ function getRepositoryComplete(data, status) {
+ vm.repositories = data || [];
+ $scope.$broadcast('refreshTags', true);
+ }
+
+ function getRepositoryFailed(response) {
+ console.log('Failed to list repositories:' + response);
+ }
+
+ function deleteByRepo(repoName) {
+ vm.repoName = repoName;
+ vm.tag = '';
+
+ $scope.$emit('modalTitle', $filter('tr')('alert_delete_repo_title', [repoName]));
+ $scope.$emit('modalMessage', $filter('tr')('alert_delete_repo', [repoName]));
+
+ var emitInfo = {
+ 'confirmOnly': false,
+ 'contentType': 'text/html',
+ 'action' : vm.deleteImage
+ };
+
+ $scope.$emit('raiseInfo', emitInfo);
+ }
+
+ function deleteByTag() {
+ $scope.$emit('modalTitle', $filter('tr')('alert_delete_tag_title', [vm.tag]));
+ var message;
+ console.log('vm.tagCount:' + angular.toJson(vm.tagCount[vm.repoName]));
+ $scope.$emit('modalMessage', $filter('tr')('alert_delete_tag', [vm.tag]));
+
+ var emitInfo = {
+ 'confirmOnly': false,
+ 'contentType': 'text/html',
+ 'action' : vm.deleteImage
+ };
+
+ $scope.$emit('raiseInfo', emitInfo);
+ }
+
+ function deleteImage() {
+
+ console.log('Delete image, repoName:' + vm.repoName + ', tag:' + vm.tag);
+ vm.toggleInProgress[vm.repoName + '|' + vm.tag] = true;
+ DeleteRepositoryService(vm.repoName, vm.tag)
+ .success(deleteRepositorySuccess)
+ .error(deleteRepositoryFailed);
+ }
+
+ function deleteRepositorySuccess(data, status) {
+ vm.toggleInProgress[vm.repoName + '|' + vm.tag] = false;
+ vm.retrieve();
+ }
+
+ function deleteRepositoryFailed(data, status) {
+ vm.toggleInProgress[vm.repoName + '|' + vm.tag] = false;
+
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ var message;
+ if(status === 401) {
+ message = $filter('tr')('failed_to_delete_repo_insuffient_permissions');
+ }else{
+ message = $filter('tr')('failed_to_delete_repo');
+ }
+ $scope.$emit('modalMessage', message);
+ $scope.$emit('raiseError', true);
+
+ console.log('Failed to delete repository:' + angular.toJson(data));
+ }
+
+ }
+
+ function listRepository() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/repository/list-repository.directive.html',
+ 'scope': {
+ 'sectionHeight': '='
+ },
+ 'link': link,
+ 'controller': ListRepositoryController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+
+ return directive;
+
+ function link(scope, element, attr, ctrl) {
+
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/repository/list-tag.directive.html b/static/resources/js/components/repository/list-tag.directive.html
new file mode 100644
index 000000000..94fc5dab0
--- /dev/null
+++ b/static/resources/js/components/repository/list-tag.directive.html
@@ -0,0 +1,22 @@
+
+
+
+
+
// 'tag' | tr //
+
// 'image_details' | tr //
+
// 'pull_command' | tr //
+
// 'operation' | tr //
+
+
+
+
//tag//
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/repository/list-tag.directive.js b/static/resources/js/components/repository/list-tag.directive.js
new file mode 100644
index 000000000..c856512f5
--- /dev/null
+++ b/static/resources/js/components/repository/list-tag.directive.js
@@ -0,0 +1,100 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.repository')
+ .directive('listTag', listTag);
+
+ ListTagController.$inject = ['$scope', 'ListTagService', '$filter', 'trFilter'];
+
+ function ListTagController($scope, ListTagService, $filter, trFilter) {
+ var vm = this;
+
+ vm.tags = [];
+ vm.retrieve = retrieve;
+
+ $scope.$watch('vm.repoName', function(current, origin) {
+ if(current) {
+ console.log('vm.repoName in tags:' + current);
+ vm.retrieve();
+ }
+ });
+
+ $scope.$on('refreshTags', function(e, val) {
+ if(val) {
+ vm.retrieve();
+ }
+ });
+
+ vm.deleteTag = deleteTag;
+
+ function retrieve() {
+ ListTagService(vm.repoName)
+ .success(getTagSuccess)
+ .error(getTagFailed);
+ }
+
+ function getTagSuccess(data) {
+
+ vm.tags = data || [];
+ vm.tagCount[vm.repoName] = vm.tags.length;
+
+ $scope.$emit('tags', vm.tags);
+ $scope.$emit('tagCount', vm.tagCount);
+
+ angular.forEach(vm.tags, function(item) {
+ vm.toggleInProgress[vm.repoName + '|' + item] = false;
+ });
+ }
+
+ 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:' + data);
+ }
+
+ function deleteTag(e) {
+ $scope.$emit('repoName', e.repoName);
+ $scope.$emit('tag', e.tag);
+ vm.deleteByTag();
+ }
+
+ }
+
+ function listTag() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/repository/list-tag.directive.html',
+ 'scope': {
+ 'tagCount': '=',
+ 'associateId': '=',
+ 'repoName': '=',
+ 'toggleInProgress': '=',
+ 'deleteByTag': '&'
+ },
+ 'replace': true,
+ 'controller': ListTagController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/repository/popup-details.directive.html b/static/resources/js/components/repository/popup-details.directive.html
new file mode 100644
index 000000000..83d77b26f
--- /dev/null
+++ b/static/resources/js/components/repository/popup-details.directive.html
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/static/resources/js/components/repository/popup-details.directive.js b/static/resources/js/components/repository/popup-details.directive.js
new file mode 100644
index 000000000..59311917a
--- /dev/null
+++ b/static/resources/js/components/repository/popup-details.directive.js
@@ -0,0 +1,113 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.repository')
+ .directive('popupDetails', popupDetails);
+
+ PopupDetailsController.$inject = ['ListManifestService', '$filter', 'dateLFilter'];
+
+ function PopupDetailsController(ListManifestService, $filter, dateLFilter) {
+ var vm = this;
+
+ vm.retrieve = retrieve;
+
+ function retrieve() {
+ ListManifestService(vm.repoName, vm.tag)
+ .success(getManifestSuccess)
+ .error(getManifestFailed);
+ }
+
+ function getManifestSuccess(data, status) {
+ console.log('Successful get manifest:' + data);
+ vm.manifest = data;
+ vm.manifest['Created'] = $filter('dateL')(vm.manifest['Created'], 'YYYY-MM-DD HH:mm:ss');
+ }
+
+ function getManifestFailed(data, status) {
+ console.log('Failed to get manifest:' + data);
+ }
+ }
+
+ function popupDetails() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/repository/popup-details.directive.html',
+ 'scope': {
+ 'repoName': '@',
+ 'tag': '@',
+ 'index': '@'
+ },
+ 'replace': true,
+ 'link': link,
+ 'controller': PopupDetailsController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+ ctrl.retrieve();
+ scope.$watch('vm.manifest', function(current) {
+ if(current) {
+
+ element
+ .popover({
+ 'template': '
',
+ 'title': '
',
+ 'content': generateContent,
+ 'html': true
+ })
+ .on('shown.bs.popover', function(e){
+ var self = jQuery(this);
+ $('[type="text"]:input', self.parent())
+ .on('click', function() {
+ $(this).select();
+ });
+ self.parent().find('.glyphicon.glyphicon-remove-circle').on('click', function() {
+ element.trigger('click');
+ });
+ });
+ }
+ });
+ function generateContent() {
+ var content = '';
+ return content;
+ }
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/repository/pull-command.directive.html b/static/resources/js/components/repository/pull-command.directive.html
new file mode 100644
index 000000000..0a2b07f3a
--- /dev/null
+++ b/static/resources/js/components/repository/pull-command.directive.html
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/static/resources/js/components/repository/pull-command.directive.js b/static/resources/js/components/repository/pull-command.directive.js
new file mode 100644
index 000000000..a002f011c
--- /dev/null
+++ b/static/resources/js/components/repository/pull-command.directive.js
@@ -0,0 +1,55 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.repository')
+ .directive('pullCommand', pullCommand);
+
+ function PullCommandController() {
+
+ }
+
+ function pullCommand() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/repository/pull-command.directive.html',
+ 'scope': {
+ 'repoName': '@',
+ 'tag': '@'
+ },
+ 'link': link,
+ 'controller': PullCommandController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+
+ ctrl.harborRegUrl = $('#HarborRegUrl').val() + '/';
+
+ element.find('a').on('click', clickHandler);
+ function clickHandler(e) {
+ element.find('input[type="text"]').select();
+ }
+
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/repository/repository.module.js b/static/resources/js/components/repository/repository.module.js
new file mode 100644
index 000000000..10b3ee18a
--- /dev/null
+++ b/static/resources/js/components/repository/repository.module.js
@@ -0,0 +1,21 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.repository', [
+ 'harbor.services.repository']);
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/search/search-input.directive.html b/static/resources/js/components/search/search-input.directive.html
new file mode 100644
index 000000000..dadde61ef
--- /dev/null
+++ b/static/resources/js/components/search/search-input.directive.html
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/search/search-input.directive.js b/static/resources/js/components/search/search-input.directive.js
new file mode 100644
index 000000000..5fd9173d5
--- /dev/null
+++ b/static/resources/js/components/search/search-input.directive.js
@@ -0,0 +1,68 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.search')
+ .directive('searchInput', searchInput);
+
+ SearchInputController.$inject = ['$scope', '$location', '$window'];
+
+ function SearchInputController($scope, $location, $window) {
+ var vm = this;
+
+ vm.searchFor = searchFor;
+
+ function searchFor(searchContent) {
+ $location
+ .path('/search')
+ .search({'q': searchContent});
+ $window.location.href = $location.url();
+ }
+
+ }
+
+ function searchInput() {
+
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/search/search-input.directive.html',
+ 'scope': {
+ 'searchInput': '=',
+ },
+ 'link': link,
+ 'controller': SearchInputController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+ element
+ .find('input[type="text"]')
+ .on('keydown', keydownHandler);
+
+ function keydownHandler(e) {
+ if(e.keyCode === 13) {
+ ctrl.searchFor($(this).val());
+ }
+ }
+
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/search/search.directive.html b/static/resources/js/components/search/search.directive.html
new file mode 100644
index 000000000..9086c0bba
--- /dev/null
+++ b/static/resources/js/components/search/search.directive.html
@@ -0,0 +1,26 @@
+
+
+
+
+
// 'project_repo_name' | tr //
// 'creation_time' | tr //
// 'author' | tr //
+
+
+
+
//s.repository_name//
N/A
N/A
+
+
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/search/search.directive.js b/static/resources/js/components/search/search.directive.js
new file mode 100644
index 000000000..66182aa43
--- /dev/null
+++ b/static/resources/js/components/search/search.directive.js
@@ -0,0 +1,66 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.search')
+ .directive('search', search);
+
+ SearchController.$inject = ['SearchService', '$scope'];
+
+ function SearchController(SearchService, $scope) {
+ var vm = this;
+ vm.keywords = "";
+ vm.search = searchByFilter;
+ vm.filterBy = 'repository';
+
+ searchByFilter();
+
+
+ function searchByFilter() {
+ SearchService(vm.keywords)
+ .success(searchSuccess)
+ .error(searchFailed);
+ }
+
+ function searchSuccess(data, status) {
+ console.log('filterBy:' + vm.filterBy + ", data:" + data);
+ vm.searchResult = data[vm.filterBy];
+ }
+
+ function searchFailed(data, status) {
+ console.log('Failed to search:' + data);
+ }
+
+ }
+
+ function search() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/search/search.directive.html',
+ 'scope': {
+ 'filterBy': '='
+ },
+ 'controller': SearchController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+
+ return directive;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/search/search.module.js b/static/resources/js/components/search/search.module.js
new file mode 100644
index 000000000..5be6cf0a3
--- /dev/null
+++ b/static/resources/js/components/search/search.module.js
@@ -0,0 +1,21 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.search', [
+ 'harbor.services.search']);
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/sign-in/sign-in.directive.js b/static/resources/js/components/sign-in/sign-in.directive.js
new file mode 100644
index 000000000..885239cfd
--- /dev/null
+++ b/static/resources/js/components/sign-in/sign-in.directive.js
@@ -0,0 +1,115 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.sign.in')
+ .directive('signIn', signIn);
+
+ SignInController.$inject = ['SignInService', 'LogOutService', 'currentUser', 'I18nService', '$window', '$scope', 'getParameterByName', '$location'];
+ function SignInController(SignInService, LogOutService, currentUser, I18nService, $window, $scope, getParameterByName, $location) {
+ var vm = this;
+
+ vm.hasError = false;
+ vm.errorMessage = '';
+
+ vm.reset = reset;
+ vm.doSignIn = doSignIn;
+ vm.doSignUp = doSignUp;
+ vm.doForgotPassword = doForgotPassword;
+
+ vm.doContinue = doContinue;
+ vm.doLogOut = doLogOut;
+
+ vm.signInTIP = false;
+
+ function reset() {
+ vm.hasError = false;
+ vm.errorMessage = '';
+ }
+
+ function doSignIn(user) {
+ if(user && angular.isDefined(user.principal) && angular.isDefined(user.password)) {
+ vm.lastUrl = getParameterByName('last_url', $location.absUrl());
+ vm.signInTIP = true;
+ SignInService(user.principal, user.password)
+ .success(signedInSuccess)
+ .error(signedInFailed);
+ }
+ }
+
+ function signedInSuccess(data, status) {
+ if(vm.lastUrl) {
+ $window.location.href = vm.lastUrl;
+ return;
+ }
+ $window.location.href = "/dashboard";
+ }
+
+ function signedInFailed(data, status) {
+ vm.signInTIP = false;
+ if(status === 401) {
+ vm.hasError = true;
+ vm.errorMessage = 'username_or_password_is_incorrect';
+ }
+ console.log('Failed to sign in:' + data + ', status:' + status);
+ }
+
+ function doSignUp() {
+ $window.location.href = '/sign_up';
+ }
+
+ function doForgotPassword() {
+ $window.location.href = '/forgot_password';
+ }
+
+ function doContinue() {
+ $window.location.href = '/dashboard';
+ }
+
+ function doLogOut() {
+ LogOutService()
+ .success(logOutSuccess)
+ .error(logOutFailed);
+ }
+
+ function logOutSuccess(data, status) {
+ currentUser.unset();
+ I18nService().unset();
+ $window.location.href= '/';
+ }
+
+ function logOutFailed(data, status) {
+ console.log('Failed to to log out:' + data);
+ }
+ }
+
+ function signIn() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/sign_in',
+ 'scope': true,
+ 'controller': SignInController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+
+ return directive;
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/sign-in/sign-in.module.js b/static/resources/js/components/sign-in/sign-in.module.js
new file mode 100644
index 000000000..5c257f635
--- /dev/null
+++ b/static/resources/js/components/sign-in/sign-in.module.js
@@ -0,0 +1,24 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.sign.in', [
+ 'harbor.services.user'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/summary/summary.directive.html b/static/resources/js/components/summary/summary.directive.html
new file mode 100644
index 000000000..a24ff5221
--- /dev/null
+++ b/static/resources/js/components/summary/summary.directive.html
@@ -0,0 +1,19 @@
+
+
diff --git a/static/resources/js/components/summary/summary.directive.js b/static/resources/js/components/summary/summary.directive.js
new file mode 100644
index 000000000..2273650c7
--- /dev/null
+++ b/static/resources/js/components/summary/summary.directive.js
@@ -0,0 +1,56 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.summary')
+ .directive('projectSummary', projectSummary);
+
+ ProjectSummaryController.$inject = ['$scope', 'StatProjectService', '$filter', 'trFilter'];
+
+ function ProjectSummaryController($scope, StatProjectService, $filter, trFilter) {
+ var vm = this;
+
+ StatProjectService()
+ .success(statProjectSuccess)
+ .error(statProjectFailed);
+
+ function statProjectSuccess(data) {
+ vm.statProjects = data;
+ }
+
+ function statProjectFailed(data) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_get_stat') + data);
+ $scope.$emit('raiseError', true);
+ console.log('Failed to get stat:' + data);
+ }
+ }
+
+ function projectSummary() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/summary/summary.directive.html',
+ 'controller': ProjectSummaryController,
+ 'scope' : true,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/summary/summary.module.js b/static/resources/js/components/summary/summary.module.js
new file mode 100644
index 000000000..3828e956d
--- /dev/null
+++ b/static/resources/js/components/summary/summary.module.js
@@ -0,0 +1,24 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.summary', [
+ 'harbor.services.project'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/system-management/configuration.directive.html b/static/resources/js/components/system-management/configuration.directive.html
new file mode 100644
index 000000000..74d2a5b9b
--- /dev/null
+++ b/static/resources/js/components/system-management/configuration.directive.html
@@ -0,0 +1,76 @@
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/system-management/configuration.directive.js b/static/resources/js/components/system-management/configuration.directive.js
new file mode 100644
index 000000000..bc21345ad
--- /dev/null
+++ b/static/resources/js/components/system-management/configuration.directive.js
@@ -0,0 +1,68 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.system.management')
+ .directive('configuration', configuration);
+
+ ConfigurationController.$inject = [];
+
+ function ConfigurationController() {
+ var vm = this;
+
+ vm.registrationOptions = [
+ {
+ 'name': 'on',
+ 'value': true
+ },
+ {
+ 'name': 'off',
+ 'value': false
+ }
+ ];
+ vm.currentRegistration = {
+ 'name': 'on',
+ 'value': true
+ };
+
+ vm.changeSettings = changeSettings;
+
+ vm.selectRegistration = selectRegistration;
+
+ function selectRegistration() {
+
+ }
+
+ function changeSettings(system) {
+ console.log(system);
+ }
+ }
+
+ function configuration() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/system-management/configuration.directive.html',
+ 'scope': true,
+ 'controller': ConfigurationController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/system-management/create-destination.directive.html b/static/resources/js/components/system-management/create-destination.directive.html
new file mode 100644
index 000000000..1f0d8bd5a
--- /dev/null
+++ b/static/resources/js/components/system-management/create-destination.directive.html
@@ -0,0 +1,84 @@
+
+
+
+
+
+
diff --git a/static/resources/js/components/system-management/create-destination.directive.js b/static/resources/js/components/system-management/create-destination.directive.js
new file mode 100644
index 000000000..2d857bc27
--- /dev/null
+++ b/static/resources/js/components/system-management/create-destination.directive.js
@@ -0,0 +1,249 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.system.management')
+ .directive('createDestination', createDestination);
+
+ CreateDestinationController.$inject = ['$scope', 'ListDestinationService', 'CreateDestinationService', 'UpdateDestinationService', 'PingDestinationService', 'ListDestinationPolicyService', '$filter', 'trFilter', '$timeout'];
+
+ function CreateDestinationController($scope, ListDestinationService, CreateDestinationService, UpdateDestinationService, PingDestinationService, ListDestinationPolicyService, $filter, trFilter, $timeout) {
+ var vm = this;
+
+ $scope.destination = {};
+
+ var vm0 = $scope.destination;
+ vm.addNew = addNew;
+ vm.edit = edit;
+ vm.create = create;
+ vm.update = update;
+ vm.pingDestination = pingDestination;
+
+ vm.editable = true;
+ vm.notAvailable = false;
+ vm.pingAvailable = true;
+ vm.pingMessage = '';
+
+ vm.closeError = closeError;
+ vm.toggleErrorMessage = false;
+ vm.errorMessages = [];
+
+ vm.pingTIP = false;
+
+ $scope.$watch('destination.endpoint', function(current) {
+ if(current) {
+ vm.notAvailable = false;
+ }else{
+ vm.notAvailable = true;
+ }
+ });
+
+ function addNew() {
+ vm.modalTitle = $filter('tr')('add_new_destination', []);
+ vm0.name = '';
+ vm0.endpoint = '';
+ vm0.username = '';
+ vm0.password = '';
+ }
+
+ function edit(targetId) {
+ vm.editable = true;
+ vm.modalTitle = $filter('tr')('edit_destination', []);
+ ListDestinationService(targetId)
+ .success(getDestinationSuccess)
+ .error(getDestinationFailed);
+ }
+
+ function create(destination) {
+ CreateDestinationService(destination.name, destination.endpoint,
+ destination.username, destination.password)
+ .success(createDestinationSuccess)
+ .error(createDestinationFailed);
+ }
+
+ function createDestinationSuccess(data, status) {
+ console.log('Successful created destination.');
+ vm.reload();
+ }
+
+ function createDestinationFailed(data, status) {
+ if(status === 409) {
+ vm.errorMessages.push($filter('tr')('destination_already_exists'));
+ }else{
+ vm.errorMessages.push($filter('tr')('failed_to_create_destination') + data);
+ }
+ console.log('Failed to create destination:' + data);
+ }
+
+ function update(destination) {
+ UpdateDestinationService(vm.targetId, destination)
+ .success(updateDestinationSuccess)
+ .error(updateDestinationFailed);
+ }
+
+ function updateDestinationSuccess(data, status) {
+ console.log('Successful update destination.');
+ vm.reload();
+ }
+
+ function updateDestinationFailed(data, status) {
+ vm.errorMessages.push($filter('tr')('failed_to_update_destination') + data);
+ console.log('Failed to update destination.');
+ }
+
+
+ function getDestinationSuccess(data, status) {
+ var destination = data;
+ vm0.name = destination.name;
+ vm0.endpoint = destination.endpoint;
+ vm0.username = destination.username;
+ vm0.password = destination.password;
+
+ ListDestinationPolicyService(destination.id)
+ .success(listDestinationPolicySuccess)
+ .error(listDestinationPolicyFailed);
+ }
+
+ function getDestinationFailed(data, status) {
+ vm.errorMessages.push($filter('tr')('failed_get_destination'));
+ console.log('Failed to get destination.');
+ }
+
+ function listDestinationPolicySuccess(data, status) {
+ for(var i in data) {
+ if(data[i].enabled === 1) {
+ vm.editable = false;
+ break;
+ }
+ }
+ }
+
+ function listDestinationPolicyFailed(data, status) {
+ vm.errorMessages.push($filter('tr')('failed_get_destination_policies'));
+ console.log('Failed to list destination policy:' + data);
+ }
+
+ function pingDestination() {
+
+ vm.pingTIP = true;
+ vm.pingAvailable = false;
+
+ var target = {
+ 'name': vm0.name,
+ 'endpoint': vm0.endpoint,
+ 'username': vm0.username,
+ 'password': vm0.password
+ };
+ PingDestinationService(target)
+ .success(pingDestinationSuccess)
+ .error(pingDestinationFailed);
+ }
+
+ function closeError() {
+ vm.errorMessages = [];
+ vm.toggleErrorMessage = false;
+ }
+
+ function pingDestinationSuccess(data, status) {
+ vm.pingAvailable = true;
+ vm.pingTIP = false;
+ vm.pingMessage = $filter('tr')('successful_ping_target', []);
+ }
+ function pingDestinationFailed(data, status) {
+
+ vm.pingTIP = false;
+ vm.pingMessage = $filter('tr')('failed_to_ping_target', []) + (data && data.length > 0 ? ':' + data : '');
+ }
+ }
+
+ function createDestination($timeout) {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/system-management/create-destination.directive.html',
+ 'scope': {
+ 'action': '@',
+ 'targetId': '@',
+ 'reload': '&'
+ },
+ 'link': link,
+ 'controller': CreateDestinationController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+
+ element.find('#createDestinationModal').on('show.bs.modal', function() {
+ 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;
+ }
+
+ scope.$watch('vm.errorMessages', function(current) {
+ if(current && current.length > 0) {
+ ctrl.toggleErrorMessage = true;
+ }
+ }, true);
+
+ });
+ });
+
+ ctrl.save = save;
+
+ function save(destination) {
+ if(destination) {
+ ctrl.toggleErrorMessage = false;
+ ctrl.errorMessages = [];
+
+ switch(ctrl.action) {
+ case 'ADD_NEW':
+ ctrl.create(destination);
+ break;
+ case 'EDIT':
+ ctrl.update(destination);
+ break;
+ }
+
+ $timeout(function() {
+ if(!ctrl.toggleErrorMessage) {
+ element.find('#createDestinationModal').modal('hide');
+ }
+ }, 50);
+ }
+ }
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/system-management/destination.directive.html b/static/resources/js/components/system-management/destination.directive.html
new file mode 100644
index 000000000..37774c994
--- /dev/null
+++ b/static/resources/js/components/system-management/destination.directive.html
@@ -0,0 +1,63 @@
+
+
diff --git a/static/resources/js/components/system-management/replication.directive.js b/static/resources/js/components/system-management/replication.directive.js
new file mode 100644
index 000000000..265e010ca
--- /dev/null
+++ b/static/resources/js/components/system-management/replication.directive.js
@@ -0,0 +1,93 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.system.management')
+ .directive('replication', replication);
+
+ ReplicationController.$inject = ['$scope', 'ListReplicationPolicyService', 'ToggleReplicationPolicyService', '$filter', 'trFilter'];
+
+ function ReplicationController($scope, ListReplicationPolicyService, ToggleReplicationPolicyService, $filter, trFilter) {
+
+ $scope.subsSubPane = 276;
+
+ var vm = this;
+ vm.retrieve = retrieve;
+ vm.search = search;
+ vm.togglePolicy = togglePolicy;
+ vm.editReplication = editReplication;
+ vm.retrieve();
+
+ function search() {
+ vm.retrieve();
+ }
+
+ function retrieve() {
+ ListReplicationPolicyService('', '', vm.replicationName)
+ .success(listReplicationPolicySuccess)
+ .error(listReplicationPolicyFailed);
+ }
+
+ function listReplicationPolicySuccess(data, status) {
+ vm.replications = data || [];
+ }
+
+ function listReplicationPolicyFailed(data, status) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_list_replication'));
+ $scope.$emit('raiseError', true);
+ console.log('Failed to list replication policy.');
+ }
+
+ function togglePolicy(policyId, enabled) {
+ ToggleReplicationPolicyService(policyId, enabled)
+ .success(toggleReplicationPolicySuccess)
+ .error(toggleReplicationPolicyFailed);
+ }
+
+ function toggleReplicationPolicySuccess(data, status) {
+ console.log('Successful toggle replication policy.');
+ vm.retrieve();
+ }
+
+ function toggleReplicationPolicyFailed(data, status) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_toggle_policy'));
+ $scope.$emit('raiseError', true);
+ console.log('Failed to toggle replication policy.');
+ }
+
+ function editReplication(policyId) {
+ vm.action = 'EDIT';
+ vm.policyId = policyId;
+ }
+ }
+
+ function replication() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/system-management/replication.directive.html',
+ 'scope': true,
+ 'controller': ReplicationController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/system-management/system-management.directive.html b/static/resources/js/components/system-management/system-management.directive.html
new file mode 100644
index 000000000..2005152c2
--- /dev/null
+++ b/static/resources/js/components/system-management/system-management.directive.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/static/resources/js/components/system-management/system-management.directive.js b/static/resources/js/components/system-management/system-management.directive.js
new file mode 100644
index 000000000..68d17938d
--- /dev/null
+++ b/static/resources/js/components/system-management/system-management.directive.js
@@ -0,0 +1,54 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.system.management')
+ .directive('systemManagement', systemManagement);
+
+ SystemManagementController.$inject = ['$scope', '$location'];
+
+ function SystemManagementController($scope, $location) {
+ var vm = this;
+ var currentTarget = $location.path().substring(1);
+
+ switch(currentTarget) {
+ case 'destinations':
+ case 'replication':
+ $location.path('/' + currentTarget);
+ vm.target = currentTarget;
+ break;
+ default:
+ $location.path('/destinations');
+ vm.target = 'destinations';
+ }
+
+ }
+
+ function systemManagement() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/system-management/system-management.directive.html',
+ 'scope': true,
+ 'controller': SystemManagementController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/system-management/system-management.module.js b/static/resources/js/components/system-management/system-management.module.js
new file mode 100644
index 000000000..f52f7aa1a
--- /dev/null
+++ b/static/resources/js/components/system-management/system-management.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.system.management', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/top-repository/top-repository.directive.html b/static/resources/js/components/top-repository/top-repository.directive.html
new file mode 100644
index 000000000..c45a7adfa
--- /dev/null
+++ b/static/resources/js/components/top-repository/top-repository.directive.html
@@ -0,0 +1,38 @@
+
+
// 'popular_repositories' | tr //
+
+
+
+
+
// 'repository_name' | tr //
+
// 'count' | tr //
+
// 'creator' | tr //
+
+
+
+
+
+
+
+
// 'no_top_repositories' | tr //
+
+
+
//t.name//
//t.count//
//t.creator === '' ? '-' : t.creator //
+
+
+
+
+
diff --git a/static/resources/js/components/top-repository/top-repository.directive.js b/static/resources/js/components/top-repository/top-repository.directive.js
new file mode 100644
index 000000000..800f7e767
--- /dev/null
+++ b/static/resources/js/components/top-repository/top-repository.directive.js
@@ -0,0 +1,58 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.top.repository')
+ .directive('topRepository', topRepository);
+
+ TopRepositoryController.$inject = ['$scope', 'ListTopRepositoryService', '$filter', 'trFilter'];
+
+ function TopRepositoryController($scope, ListTopRepositoryService, $filter, trFilter) {
+ var vm = this;
+
+ ListTopRepositoryService(5)
+ .success(listTopRepositorySuccess)
+ .error(listTopRepositoryFailed);
+
+ function listTopRepositorySuccess(data) {
+ vm.top10Repositories = data || [];
+ }
+
+ function listTopRepositoryFailed(data, status) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_get_top_repo'));
+ $scope.$emit('raiseError', true);
+ console.log('Failed to get top repo:' + data);
+ }
+ }
+
+ function topRepository() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/top-repository/top-repository.directive.html',
+ 'controller': TopRepositoryController,
+ 'scope' : {
+ 'customBodyHeight': '='
+ },
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+ }
+
+})();
diff --git a/static/resources/js/components/top-repository/top-repository.module.js b/static/resources/js/components/top-repository/top-repository.module.js
new file mode 100644
index 000000000..988f4e2f6
--- /dev/null
+++ b/static/resources/js/components/top-repository/top-repository.module.js
@@ -0,0 +1,24 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.top.repository', [
+ 'harbor.services.repository'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/user-log/user-log.directive.html b/static/resources/js/components/user-log/user-log.directive.html
new file mode 100644
index 000000000..5500b0ffd
--- /dev/null
+++ b/static/resources/js/components/user-log/user-log.directive.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
// 'operation' | tr //
+
// 'details' | tr //
+
// 'user' | tr //
+
// 'creation_time' | tr //
+
+
+
+
+
+
+
+
// 'no_user_logs' | tr //
+
+
+
//t.operation//
//t.repo_name//
//t.username//
//t.op_time | dateL : 'YYYY-MM-DD HH:mm:ss'//
+
+
+
+
+
diff --git a/static/resources/js/components/user-log/user-log.directive.js b/static/resources/js/components/user-log/user-log.directive.js
new file mode 100644
index 000000000..b735b00ef
--- /dev/null
+++ b/static/resources/js/components/user-log/user-log.directive.js
@@ -0,0 +1,57 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.user.log')
+ .directive('userLog', userLog);
+
+ UserLogController.$inject = ['$scope', 'ListIntegratedLogService', '$filter', 'trFilter'];
+
+ function UserLogController($scope, ListIntegratedLogService, $filter, trFilter) {
+ var vm = this;
+
+ ListIntegratedLogService()
+ .success(listIntegratedLogSuccess)
+ .error(listIntegratedLogFailed);
+
+ function listIntegratedLogSuccess(data) {
+ vm.integratedLogs = data || []
+ }
+
+ function listIntegratedLogFailed(data, status) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_get_user_log') + data);
+ $scope.$emit('raiseError', true);
+ console.log('Failed to get user logs:' + data);
+ }
+ }
+
+ function userLog() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/user-log/user-log.directive.html',
+ 'controller': UserLogController,
+ 'scope' : true,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+
+ return directive;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/user-log/user-log.module.js b/static/resources/js/components/user-log/user-log.module.js
new file mode 100644
index 000000000..6610262b1
--- /dev/null
+++ b/static/resources/js/components/user-log/user-log.module.js
@@ -0,0 +1,24 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.user.log', [
+ 'harbor.services.log'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/user/list-user.directive.html b/static/resources/js/components/user/list-user.directive.html
new file mode 100644
index 000000000..f84eedc46
--- /dev/null
+++ b/static/resources/js/components/user/list-user.directive.html
@@ -0,0 +1,64 @@
+
+
+
+
diff --git a/static/resources/js/components/user/list-user.directive.js b/static/resources/js/components/user/list-user.directive.js
new file mode 100644
index 000000000..bbd7c929c
--- /dev/null
+++ b/static/resources/js/components/user/list-user.directive.js
@@ -0,0 +1,110 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.user')
+ .directive('listUser', listUser);
+
+ ListUserController.$inject = ['$scope', 'ListUserService', 'DeleteUserService', '$filter', 'trFilter'];
+
+ function ListUserController($scope, ListUserService, DeleteUserService, $filter, $trFilter) {
+
+ $scope.subsSubPane = 226;
+
+ var vm = this;
+
+ vm.username = '';
+ vm.searchUser = searchUser;
+ vm.deleteUser = deleteUser;
+ vm.confirmToDelete = confirmToDelete;
+ vm.retrieve = retrieve;
+
+ vm.retrieve();
+
+ function searchUser() {
+ vm.retrieve();
+ }
+
+ function deleteUser() {
+ DeleteUserService(vm.selectedUserId)
+ .success(deleteUserSuccess)
+ .error(deleteUserFailed);
+ }
+
+ function confirmToDelete(userId, username) {
+ vm.selectedUserId = userId;
+
+ $scope.$emit('modalTitle', $filter('tr')('confirm_delete_user_title'));
+ $scope.$emit('modalMessage', $filter('tr')('confirm_delete_user', [username]));
+
+ var emitInfo = {
+ 'confirmOnly': false,
+ 'contentType': 'text/plain',
+ 'action': vm.deleteUser
+ };
+
+ $scope.$emit('raiseInfo', emitInfo);
+ }
+
+ function retrieve() {
+ ListUserService(vm.username)
+ .success(listUserSuccess)
+ .error(listUserFailed);
+ }
+
+ function deleteUserSuccess(data, status) {
+ console.log('Successful delete user.');
+ vm.retrieve();
+ }
+
+ function deleteUserFailed(data, status) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_delete_user'));
+ $scope.$emit('raiseError', true);
+ console.log('Failed to delete user.');
+ }
+
+ function listUserSuccess(data, status) {
+ vm.users = data;
+ }
+
+ function listUserFailed(data, status) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_list_user'));
+ $scope.$emit('raiseError', true);
+ console.log('Failed to list user:' + data);
+ }
+ }
+
+ function listUser() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/user/list-user.directive.html',
+ 'link': link,
+ 'controller': ListUserController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+
+ function link(scope, element, attrs) {
+
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/user/toggle-admin.directive.html b/static/resources/js/components/user/toggle-admin.directive.html
new file mode 100644
index 000000000..5c43a9d19
--- /dev/null
+++ b/static/resources/js/components/user/toggle-admin.directive.html
@@ -0,0 +1,16 @@
+
+
+
\ No newline at end of file
diff --git a/static/resources/js/components/user/toggle-admin.directive.js b/static/resources/js/components/user/toggle-admin.directive.js
new file mode 100644
index 000000000..09424b499
--- /dev/null
+++ b/static/resources/js/components/user/toggle-admin.directive.js
@@ -0,0 +1,80 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.user')
+ .directive('toggleAdmin', toggleAdmin);
+
+ ToggleAdminController.$inject = ['$scope', 'ToggleAdminService', '$filter', 'trFilter'];
+
+ function ToggleAdminController($scope, ToggleAdminService, $filter, trFilter) {
+ var vm = this;
+
+ vm.isAdmin = (vm.hasAdminRole == 1) ? true : false;
+ vm.enabled = vm.isAdmin ? 0 : 1;
+ vm.toggle = toggle;
+
+ function toggle() {
+ ToggleAdminService(vm.userId, vm.enabled)
+ .success(toggleAdminSuccess)
+ .error(toggleAdminFailed);
+ }
+
+ function toggleAdminSuccess(data, status) {
+ if(vm.isAdmin) {
+ vm.isAdmin = false;
+ }else{
+ vm.isAdmin = true;
+ }
+ console.log('Toggled userId:' + vm.userId + ' to admin:' + vm.isAdmin);
+ }
+
+ function toggleAdminFailed(data, status) {
+ $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);
+ }
+ }
+
+ function toggleAdmin() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/components/user/toggle-admin.directive.html',
+ 'scope': {
+ 'hasAdminRole': '=',
+ 'userId': '@'
+ },
+ 'link': link,
+ 'controller': ToggleAdminController,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/user/user.module.js b/static/resources/js/components/user/user.module.js
new file mode 100644
index 000000000..a81aa1614
--- /dev/null
+++ b/static/resources/js/components/user/user.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.user', [
+ 'harbor.services.user']);
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/validator/confirm-password.validator.js b/static/resources/js/components/validator/confirm-password.validator.js
new file mode 100644
index 000000000..f52050a73
--- /dev/null
+++ b/static/resources/js/components/validator/confirm-password.validator.js
@@ -0,0 +1,47 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.validator')
+ .directive('compareTo', compareTo);
+
+ function compareTo() {
+ var directive = {
+ 'require' : 'ngModel',
+ 'scope':{
+ 'otherModelValue': '=compareTo'
+ },
+ 'link': link
+ };
+ return directive;
+
+ function link (scope, element, attrs, ctrl) {
+
+ ctrl.$validators.compareTo = validator;
+
+ function validator(modelValue) {
+ return modelValue === scope.otherModelValue;
+ }
+
+ scope.$watch("otherModelValue", function(current, origin) {
+ ctrl.$validate();
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/validator/invalid-chars.validator.js b/static/resources/js/components/validator/invalid-chars.validator.js
new file mode 100644
index 000000000..9e36f3096
--- /dev/null
+++ b/static/resources/js/components/validator/invalid-chars.validator.js
@@ -0,0 +1,55 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.validator')
+ .directive('invalidChars', invalidChars);
+
+ invalidChars.$inject = ['INVALID_CHARS'];
+
+ function invalidChars(INVALID_CHARS) {
+ var directive = {
+ 'require': 'ngModel',
+ 'link': link
+ };
+
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+
+ ctrl.$validators.invalidChars = validator;
+
+ function validator(modelValue, viewValue) {
+ if(ctrl.$isEmpty(modelValue)) {
+ return true;
+ }
+
+ for(var i = 0; i < INVALID_CHARS.length; i++) {
+ if(modelValue.indexOf(INVALID_CHARS[i]) >= 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ }
+ }
+
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/validator/password.validator.js b/static/resources/js/components/validator/password.validator.js
new file mode 100644
index 000000000..3f67d7257
--- /dev/null
+++ b/static/resources/js/components/validator/password.validator.js
@@ -0,0 +1,44 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.validator')
+ .directive('password', password);
+
+ password.$inject = ['PASSWORD_REGEXP'];
+
+ function password(PASSWORD_REGEXP) {
+ var directive = {
+ 'require' : 'ngModel',
+ 'link': link
+ };
+ return directive;
+
+ function link (scope, element, attrs, ctrl) {
+
+ ctrl.$validators.password = validator;
+
+ function validator(modelValue, viewValue) {
+
+ return PASSWORD_REGEXP.test(modelValue);
+
+ }
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/validator/project-name.validator.js b/static/resources/js/components/validator/project-name.validator.js
new file mode 100644
index 000000000..3c36c5827
--- /dev/null
+++ b/static/resources/js/components/validator/project-name.validator.js
@@ -0,0 +1,41 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.validator')
+ .directive('projectName', projectName);
+
+ projectName.$inject = ['PROJECT_REGEXP']
+
+ function projectName(PROJECT_REGEXP) {
+ var directive = {
+ 'require': 'ngModel',
+ 'link': link
+ };
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+ ctrl.$validators.projectName = validator;
+
+ function validator(modelValue, viewValue) {
+ return PROJECT_REGEXP.test(modelValue);
+ }
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/validator/user-exist.validator.js b/static/resources/js/components/validator/user-exist.validator.js
new file mode 100644
index 000000000..7fa7990ec
--- /dev/null
+++ b/static/resources/js/components/validator/user-exist.validator.js
@@ -0,0 +1,70 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.validator')
+ .directive('userExists', userExists);
+
+ userExists.$inject = ['UserExistService'];
+
+ function userExists(UserExistService) {
+ var directive = {
+ 'require': 'ngModel',
+ 'scope': {
+ 'target': '@'
+ },
+ 'link': link
+ };
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+
+ var valid = true;
+
+ ctrl.$validators.userExists = validator;
+
+ function validator(modelValue, viewValue) {
+
+ console.log('modelValue:' + modelValue + ', viewValue:' + viewValue);
+
+ if(ctrl.$isEmpty(modelValue)) {
+ console.log('Model value is empty.');
+ return true;
+ }
+
+ UserExistService(attrs.target, modelValue)
+ .success(userExistSuccess)
+ .error(userExistFailed);
+
+ function userExistSuccess(data, status) {
+ valid = !data;
+ if(!valid) {
+ console.log('Model value already exists');
+ }
+ }
+
+ function userExistFailed(data, status) {
+ console.log('Failed to in retrieval:' + data);
+ }
+
+ return valid;
+ }
+ }
+
+ }
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/validator/validator.config.js b/static/resources/js/components/validator/validator.config.js
new file mode 100644
index 000000000..e9fe1cfde
--- /dev/null
+++ b/static/resources/js/components/validator/validator.config.js
@@ -0,0 +1,24 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.validator')
+ .constant('INVALID_CHARS', [",","~","#", "$", "%"])
+ .constant('PASSWORD_REGEXP', /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{7,20}$/)
+ .constant('PROJECT_REGEXP', /^[a-z0-9](?:-*[a-z0-9])*(?:[._][a-z0-9](?:-*[a-z0-9])*)*$/);
+})();
\ No newline at end of file
diff --git a/static/resources/js/components/validator/validator.module.js b/static/resources/js/components/validator/validator.module.js
new file mode 100644
index 000000000..06988ab57
--- /dev/null
+++ b/static/resources/js/components/validator/validator.module.js
@@ -0,0 +1,24 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.validator', [
+ 'harbor.services.user'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/forgot-password.js b/static/resources/js/forgot-password.js
deleted file mode 100644
index d6fdea319..000000000
--- a/static/resources/js/forgot-password.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- Copyright (c) 2016 VMware, Inc. All Rights Reserved.
- Licensed 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.
-*/
-jQuery(function(){
-
- $("#divErrMsg").css({"display": "none"});
-
- validateOptions.Items = ["#EmailF"];
- function bindEnterKey(){
- $(document).on("keydown", function(e){
- if(e.keyCode == 13){
- e.preventDefault();
- if($("#txtCommonSearch").is(":focus")){
- document.location = "/search?q=" + $("#txtCommonSearch").val();
- }else{
- $("#btnSubmit").trigger("click");
- }
- }
- });
- }
- function unbindEnterKey(){
- $(document).off("keydown");
- }
- bindEnterKey();
- var spinner = new Spinner({scale:1}).spin();
-
- $("#btnSubmit").on("click", function(){
- validateOptions.Validate(function(){
- var username = $("#UsernameF").val();
- var email = $("#EmailF").val();
- $.ajax({
- "url":"/sendEmail",
- "type": "get",
- "data": {"username": username, "email": email},
- "beforeSend": function(e){
- unbindEnterKey();
- $("h1").append(spinner.el);
- $("#btnSubmit").prop("disabled", true);
- },
- "success": function(data, status, xhr){
- if(xhr && xhr.status == 200){
- $("#dlgModal")
- .dialogModal({
- "title": i18n.getMessage("title_forgot_password"),
- "content": i18n.getMessage("email_has_been_sent"),
- "callback": function(){
- document.location="/";
- }
- });
- }
-
- },
- "complete": function(){
- spinner.stop();
- $("#btnSubmit").prop("disabled", false);
- },
- "error": function(jqXhr, status, error){
- if(jqXhr){
- $("#dlgModal")
- .dialogModal({
- "title": i18n.getMessage("title_forgot_password"),
- "content": i18n.getMessage(jqXhr.responseText),
- "callback": function(){
- bindEnterKey();
- return;
- }
- });
- }
- }
- });
- });
- });
-});
\ No newline at end of file
diff --git a/static/resources/js/harbor.config.js b/static/resources/js/harbor.config.js
new file mode 100644
index 000000000..b67001fac
--- /dev/null
+++ b/static/resources/js/harbor.config.js
@@ -0,0 +1,120 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+ angular
+ .module('harbor.app')
+ .config(function($interpolateProvider){
+ $interpolateProvider.startSymbol('//');
+ $interpolateProvider.endSymbol('//');
+ })
+ .config(function($httpProvider) {
+ $httpProvider.defaults.headers.common = {'Accept': 'application/json, text/javascript, */*; q=0.01'};
+ $httpProvider.interceptors.push('redirectInterceptor');
+ })
+ .service('redirectInterceptor', RedirectInterceptorService)
+ .factory('getParameterByName', getParameterByName)
+ .filter('dateL', localizeDate)
+ .filter('tr', tr);
+
+ RedirectInterceptorService.$inject = ['$q', '$window', '$location'];
+
+ function RedirectInterceptorService($q, $window, $location) {
+ return {
+ 'responseError': function(rejection) {
+ 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) {
+ switch(typeof(exclusion[i])) {
+ case 'string':
+ isExcluded = (exclusion[i] === url);
+ break;
+ case 'object':
+ isExcluded = exclusion[i].test(url);
+ break;
+ }
+ if(isExcluded) {
+ break;
+ }
+ }
+ if(!isExcluded && rejection.status === 401) {
+ $window.location.href = '/?last_url=' + encodeURIComponent(location.pathname + '#' + $location.url());
+ return;
+ }
+ return $q.reject(rejection);
+ }
+ };
+ }
+
+ function getParameterByName() {
+ return get;
+ function get(name, url) {
+ name = name.replace(/[\[\]]/g, "\\$&");
+ var regex = new RegExp("[?&]" + name + "(=([^]*)|&|#|$)"),
+ results = regex.exec(url);
+ if (!results) {
+ return null;
+ }
+
+ if (!results[2]) {
+ return '';
+ }
+
+ return decodeURIComponent(results[2].replace(/\+/g, " "));
+ }
+ }
+
+ function localizeDate() {
+ return filter;
+
+ function filter(input, pattern) {
+ var d = new Date(input || '');
+ if(d.getTime() <= 0) return '-';
+ return moment(d).format(pattern);
+ }
+ }
+
+ tr.$inject = ['I18nService'];
+
+ function tr(I18nService) {
+ return tr;
+ function tr(label, params) {
+ var currentLanguage = I18nService().getCurrentLanguage();
+ var result = '';
+ if(label && label.length > 0){
+ result = I18nService().getValue(label, currentLanguage);
+ }
+ if(angular.isArray(params)) {
+ angular.forEach(params, function(value, index) {
+ result = result.replace('$' + index, params[index]);
+ });
+ }
+ return result;
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/harbor.constants.js b/static/resources/js/harbor.constants.js
new file mode 100644
index 000000000..2e5449842
--- /dev/null
+++ b/static/resources/js/harbor.constants.js
@@ -0,0 +1,21 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.app');
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/harbor.data.js b/static/resources/js/harbor.data.js
new file mode 100644
index 000000000..8dc1744ae
--- /dev/null
+++ b/static/resources/js/harbor.data.js
@@ -0,0 +1,56 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.app')
+ .factory('currentUser', currentUser)
+ .factory('currentProjectMember', currentProjectMember);
+
+ currentUser.$inject = ['$cookies', '$timeout'];
+
+ function currentUser($cookies, $timeout) {
+ return {
+ set: function(user) {
+ $cookies.putObject('user', user, {'path': '/'});
+ },
+ get: function() {
+ return $cookies.getObject('user');
+ },
+ unset: function() {
+ $cookies.remove('user', {'path': '/'});
+ }
+ };
+ }
+
+ currentProjectMember.$inject = ['$cookies'];
+
+ function currentProjectMember($cookies) {
+ return {
+ set: function(member) {
+ $cookies.putObject('member', member, {'path': '/'});
+ },
+ get: function() {
+ return $cookies.getObject('member');
+ },
+ unset: function() {
+ $cookies.remove('member', {'path': '/'});
+ }
+ };
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/harbor.initialize.js b/static/resources/js/harbor.initialize.js
new file mode 100644
index 000000000..c267bd383
--- /dev/null
+++ b/static/resources/js/harbor.initialize.js
@@ -0,0 +1,21 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.app');
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/harbor.module.js b/static/resources/js/harbor.module.js
new file mode 100644
index 000000000..500ae0f58
--- /dev/null
+++ b/static/resources/js/harbor.module.js
@@ -0,0 +1,65 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+ angular
+ .module('harbor.app', [
+ 'ngMessages',
+ 'ngCookies',
+ 'harbor.session',
+ 'harbor.layout.element.height',
+ 'harbor.layout.header',
+ 'harbor.layout.footer',
+ 'harbor.layout.navigation',
+ 'harbor.layout.sign.up',
+ 'harbor.layout.add.new',
+ 'harbor.layout.account.setting',
+ 'harbor.layout.change.password',
+ 'harbor.layout.forgot.password',
+ 'harbor.layout.reset.password',
+ 'harbor.layout.index',
+ 'harbor.layout.dashboard',
+ 'harbor.layout.project',
+ 'harbor.layout.admin.option',
+ 'harbor.layout.search',
+ 'harbor.services.i18n',
+ 'harbor.services.project',
+ 'harbor.services.user',
+ 'harbor.services.repository',
+ 'harbor.services.project.member',
+ 'harbor.services.replication.policy',
+ 'harbor.services.replication.job',
+ 'harbor.services.destination',
+ 'harbor.summary',
+ 'harbor.user.log',
+ 'harbor.top.repository',
+ 'harbor.optional.menu',
+ 'harbor.modal.dialog',
+ 'harbor.sign.in',
+ 'harbor.search',
+ 'harbor.project',
+ 'harbor.details',
+ 'harbor.repository',
+ 'harbor.project.member',
+ 'harbor.user',
+ 'harbor.log',
+ 'harbor.validator',
+ 'harbor.replication',
+ 'harbor.system.management',
+ 'harbor.loading.progress',
+ 'harbor.inline.help',
+ 'harbor.dismissable.alerts'
+ ]);
+})();
diff --git a/static/resources/js/item-detail.js b/static/resources/js/item-detail.js
deleted file mode 100644
index bfab81536..000000000
--- a/static/resources/js/item-detail.js
+++ /dev/null
@@ -1,473 +0,0 @@
-/*
- Copyright (c) 2016 VMware, Inc. All Rights Reserved.
- Licensed 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.
-*/
-jQuery(function(){
-
- $.when(
- new AjaxUtil({
- url: "/api/users/current",
- type: "get",
- error: function(jqXhr){
- if(jqXhr){
- if(jqXhr.status == 403){
- return false;
- }
- }
- }
- }).exec()
- ).then(function(){
- noNeedToLoginCallback();
- needToLoginCallback();
- }).fail(function(){
- noNeedToLoginCallback();
- });
-
- function noNeedToLoginCallback(){
-
- $("#tabItemDetail a:first").tab("show");
- $("#btnFilterOption button:first").addClass("active");
- $("#divErrMsg").hide();
-
- if($("#public").val() == 1){
- $("#tabItemDetail li:eq(1)").hide();
- $("#tabItemDetail li:eq(2)").hide();
- }
-
- listRepo($("#repoName").val());
-
- function listRepo(repoName){
-
- $("#divErrMsg").hide();
-
- new AjaxUtil({
- url: "/api/repositories?project_id=" + $("#projectId").val() + "&q=" + repoName,
- type: "get",
- success: function(data, status, xhr){
- if(xhr && xhr.status == 200){
- $("#accordionRepo").children().remove();
- if(data == null){
- $("#divErrMsg").show();
- $("#divErrMsg center").html(i18n.getMessage("no_repo_exists"));
- return;
- }
- $.each(data, function(i, e){
- var targetId = e.replace(/\//g, "------").replace(/\./g, "---");
- var row = '
');
- });
- }
-
- function getUserRoleCallback(userId){
- new AjaxUtil({
- url: "/api/projects/" + $("#projectId").val() + "/members/" + userId,
- type: "get",
- success: function(data, status, xhr){
- var user = data;
- $("#operationType").val("edit");
- $("#editUserId").val(user.user_id);
- $("#spnSearch").hide();
- $("#txtUserName").val(user.username);
- $("#txtUserName").prop("disabled", true);
- $("#btnSave").removeClass("disabled");
- $("#dlgUserTitle").text(i18n.getMessage("edit_members"));
- $("#lstRole input[name=chooseRole]:radio").not('[value=' + user.role_id + ']').prop("checked", false)
- $.each(user.roles, function(i, e){
- $("#lstRole input[name=chooseRole]:radio").filter('[value=' + e.role_id + ']').prop("checked", "checked");
- });
- }
- }).exec();
- }
- function listUser(username){
- $.when(
- new AjaxUtil({
- url: "/api/projects/" + $("#projectId").val() + "/members?username=" + (username == null ? "" : username),
- type: "get",
- errors: {
- 403: ""
- },
- success: function(data, status, xhr){
- return data || [];
- }
- }).exec()
- ).done(function(userList){
- listUserByProjectCallback(userList || []);
- $("#tblUser .glyphicon-pencil").on("click", function(e){
- var userId = $(this).attr("userid")
- getUserRoleCallback(userId);
- });
- $("#tblUser .glyphicon-trash").on("click", function(){
- var userId = $(this).attr("userid");
- new AjaxUtil({
- url: "/api/projects/" + $("#projectId").val() + "/members/" + userId,
- type: "delete",
- complete: function(jqXhr, status){
- if(jqXhr && jqXhr.status == 200){
- listUser(null);
- }
- }
- }).exec();
- });
- });
- }
- listUser(null);
- listOperationLogs();
-
- function listOperationLogs(){
- var projectId = $("#projectId").val();
-
- $.when(
- new AjaxUtil({
- url : "/api/projects/" + projectId + "/logs/filter",
- data: {},
- type: "post",
- success: function(data){
- return data || [];
- }
- }).exec()
- ).done(function(operationLogs){
- searchAccessLogCallback(operationLogs);
- });
- }
-
- $("#btnSearchUser").on("click", function(){
- var username = $("#txtSearchUser").val();
- if($.trim(username).length == 0){
- username = null;
- }
- listUser(username);
- });
-
- function toUTCSeconds(date, hour, min, sec) {
- var t = new Date(date);
- t.setHours(hour);
- t.setMinutes(min);
- t.setSeconds(sec);
- var utcTime = new Date(t.getUTCFullYear(),
- t.getUTCMonth(),
- t.getUTCDate(),
- t.getUTCHours(),
- t.getUTCMinutes(),
- t.getUTCSeconds());
- return utcTime.getTime() / 1000;
- }
-
- $("#btnFilterLog").on("click", function(){
-
- var projectId = $("#projectId").val();
- var username = $("#txtSearchUserName").val();
-
- var beginTimestamp = 0;
- var endTimestamp = 0;
-
- if($("#begindatepicker").val() != ""){
- beginTimestamp = toUTCSeconds($("#begindatepicker").val(), 0, 0, 0);
- }
- if($("#enddatepicker").val() != ""){
- endTimestamp = toUTCSeconds($("#enddatepicker").val(), 23, 59, 59);
- }
-
- new AjaxUtil({
- url: "/api/projects/" + projectId + "/logs/filter",
- data:{"username":username, "project_id" : Number(projectId), "keywords" : getKeyWords() , "begin_timestamp" : beginTimestamp, "end_timestamp" : endTimestamp},
- type: "post",
- success: function(data, status, xhr){
- if(xhr && xhr.status == 200){
- searchAccessLogCallback(data);
- }
- }
- }).exec();
- });
-
- $("#spnFilterOption input[name=chkAll]").on("click", function(){
- $("#spnFilterOption input[name=chkOperation]").prop("checked", $(this).prop("checked"));
- });
-
- $("#spnFilterOption input[name=chkOperation]").on("click", function(){
- if(!$(this).prop("checked")){
- $("#spnFilterOption input[name=chkAll]").prop("checked", false);
- }
-
- var selectedAll = true;
-
- $("#spnFilterOption input[name=chkOperation]").each(function(i, e){
- if(!$(e).prop("checked")){
- selectedAll = false;
- }
- });
-
- if(selectedAll){
- $("#spnFilterOption input[name=chkAll]").prop("checked", true);
- }
- });
-
- function getKeyWords(){
- var keywords = "";
- var checkedItemList=$("#spnFilterOption input[name=chkOperation]:checked");
- var keywords = [];
- $.each(checkedItemList, function(i, e){
- var itemValue = $(e).val();
- if(itemValue == "others" && $.trim($("#txtOthers").val()).length > 0){
- keywords.push($("#txtOthers").val());
- }else{
- keywords.push($(e).val());
- }
- });
- return keywords.join("/");
- }
-
- $('#datetimepicker1').datetimepicker({
- locale: i18n.getLocale(),
- ignoreReadonly: true,
- format: 'L',
- showClear: true
- });
- $('#datetimepicker2').datetimepicker({
- locale: i18n.getLocale(),
- ignoreReadonly: true,
- format: 'L',
- showClear: true
- });
- });
-}
-
-$(document).on("keydown", function(e){
- if(e.keyCode == 13){
- e.preventDefault();
- if($("#tabItemDetail li:eq(0)").is(":focus") || $("#txtRepoName").is(":focus")){
- $("#btnSearchRepo").trigger("click");
- }else if($("#tabItemDetail li:eq(1)").is(":focus") || $("#txtSearchUser").is(":focus")){
- $("#btnSearchUser").trigger("click");
- }else if($("#tabItemDetail li:eq(2)").is(":focus") || $("#txtSearchUserName").is(":focus")){
- $("#btnFilterLog").trigger("click");
- }else if($("#txtUserName").is(":focus") || $("#lstRole :radio").is(":focus")){
- $("#btnSave").trigger("click");
- }
- }
-});
-})
\ No newline at end of file
diff --git a/static/resources/js/layout/account-setting/account-setting.controller.js b/static/resources/js/layout/account-setting/account-setting.controller.js
new file mode 100644
index 000000000..764117348
--- /dev/null
+++ b/static/resources/js/layout/account-setting/account-setting.controller.js
@@ -0,0 +1,112 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.account.setting')
+ .controller('AccountSettingController', AccountSettingController);
+
+ AccountSettingController.$inject = ['ChangePasswordService', 'UpdateUserService', '$filter', 'trFilter', '$scope', '$window', 'currentUser'];
+
+ function AccountSettingController(ChangePasswordService, UpdateUserService, $filter, trFilter, $scope, $window, currentUser) {
+
+ var vm = this;
+ vm.isOpen = false;
+
+ vm.hasError = false;
+ vm.errorMessage = '';
+
+ vm.reset = reset;
+ vm.confirm = confirm;
+ vm.updateUser = updateUser;
+ vm.cancel = cancel;
+
+ $scope.user = currentUser.get();
+ if(!$scope.user) {
+ $window.location.href = '/';
+ return;
+ }
+ var userId = $scope.user.user_id;
+
+ //Error message dialog handler for account setting.
+ $scope.$on('modalTitle', function(e, val) {
+ vm.modalTitle = val;
+ });
+
+ $scope.$on('modalMessage', function(e, val) {
+ vm.modalMessage = val;
+ });
+
+ $scope.$on('raiseError', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ $scope.$broadcast('showDialog', false);
+ };
+ vm.contentType = 'text/plain';
+ vm.confirmOnly = true;
+ $scope.$broadcast('showDialog', true);
+ }
+ });
+
+ function reset() {
+ $scope.form.$setUntouched();
+ $scope.form.$setPristine();
+ vm.hasError = false;
+ vm.errorMessage = '';
+ }
+
+ function confirm() {
+ $window.location.href = '/dashboard';
+ }
+
+ function updateUser(user) {
+ vm.confirmOnly = true;
+ vm.action = vm.confirm;
+ if(user && angular.isDefined(user.username) && angular.isDefined(user.realname)) {
+ UpdateUserService(userId, user)
+ .success(updateUserSuccess)
+ .error(updateUserFailed);
+ currentUser.set(user);
+ }
+ }
+
+ function updateUserSuccess(data, status) {
+ vm.modalTitle = $filter('tr')('change_profile', []);
+ vm.modalMessage = $filter('tr')('successful_changed_profile', []);
+ $scope.$broadcast('showDialog', true);
+ }
+
+ function updateUserFailed(data, status) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ var message;
+ if(status === 409) {
+ message = $filter('tr')('email_has_been_taken');
+ }else{
+ message = $filter('tr')('failed_to_update_user') + data;
+ }
+ $scope.$emit('modalMessage', message);
+ $scope.$emit('raiseError', true);
+ console.log('Failed to update user.');
+ }
+
+ function cancel(form) {
+ $window.location.href = '/dashboard';
+ }
+
+ }
+
+})();
diff --git a/static/resources/js/layout/account-setting/account-setting.module.js b/static/resources/js/layout/account-setting/account-setting.module.js
new file mode 100644
index 000000000..3b0d9df8a
--- /dev/null
+++ b/static/resources/js/layout/account-setting/account-setting.module.js
@@ -0,0 +1,23 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.account.setting', [
+ 'harbor.services.user']);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/add-new/add-new.controller.js b/static/resources/js/layout/add-new/add-new.controller.js
new file mode 100644
index 000000000..ce9c8ccd4
--- /dev/null
+++ b/static/resources/js/layout/add-new/add-new.controller.js
@@ -0,0 +1,29 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.add.new')
+ .controller('AddNewController', AddNewController);
+
+ AddNewController.$inject = [];
+
+ function AddNewController() {
+ var vm = this;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/add-new/add-new.module.js b/static/resources/js/layout/add-new/add-new.module.js
new file mode 100644
index 000000000..05e34afac
--- /dev/null
+++ b/static/resources/js/layout/add-new/add-new.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.add.new', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/admin-option/admin-option.config.js b/static/resources/js/layout/admin-option/admin-option.config.js
new file mode 100644
index 000000000..78e21a6b0
--- /dev/null
+++ b/static/resources/js/layout/admin-option/admin-option.config.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.admin.option');
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/admin-option/admin-option.controller.js b/static/resources/js/layout/admin-option/admin-option.controller.js
new file mode 100644
index 000000000..f383407c3
--- /dev/null
+++ b/static/resources/js/layout/admin-option/admin-option.controller.js
@@ -0,0 +1,94 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.admin.option')
+ .controller('AdminOptionController', AdminOptionController);
+
+ AdminOptionController.$inject = ['$scope', '$timeout', '$location'];
+
+ function AdminOptionController($scope, $timeout, $location) {
+
+ $scope.subsSubPane = 296;
+
+ var vm = this;
+ vm.toggle = false;
+ vm.target = 'users';
+ vm.toggleAdminOption = toggleAdminOption;
+
+ $scope.$on('$locationChangeSuccess', function(e) {
+ if($location.path() === '') {
+ vm.target = 'users';
+ vm.toggle = false;
+ }else{
+ vm.target = 'system_management';
+ vm.toggle = true;
+ }
+ });
+
+ //Message dialog handler for admin-options.
+ $scope.$on('modalTitle', function(e, val) {
+ vm.modalTitle = val;
+ });
+
+ $scope.$on('modalMessage', function(e, val) {
+ vm.modalMessage = val;
+ });
+
+ $scope.$on('raiseError', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ $scope.$broadcast('showDialog', false);
+ };
+ vm.contentType = 'text/plain';
+ vm.confirmOnly = true;
+
+ $timeout(function() {
+ $scope.$broadcast('showDialog', true);
+ }, 350);
+ }
+ });
+
+ $scope.$on('raiseInfo', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ val.action();
+ $scope.$broadcast('showDialog', false);
+ }
+ vm.contentType = val.contentType;
+ vm.confirmOnly = val.confirmOnly;
+
+ $scope.$broadcast('showDialog', true);
+ }
+ });
+
+
+ function toggleAdminOption(e) {
+ switch(e.target) {
+ case 'users':
+ vm.toggle = false;
+ break;
+ case 'system_management':
+ vm.toggle = true;
+ break;
+ }
+ vm.target = e.target;
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/admin-option/admin-option.module.js b/static/resources/js/layout/admin-option/admin-option.module.js
new file mode 100644
index 000000000..7f486a965
--- /dev/null
+++ b/static/resources/js/layout/admin-option/admin-option.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.admin.option', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/change-password/change-password.controller.js b/static/resources/js/layout/change-password/change-password.controller.js
new file mode 100644
index 000000000..f3c4244af
--- /dev/null
+++ b/static/resources/js/layout/change-password/change-password.controller.js
@@ -0,0 +1,114 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.change.password')
+ .controller('ChangePasswordController', ChangePasswordController);
+
+ ChangePasswordController.$inject = ['ChangePasswordService', 'UpdateUserService', '$filter', 'trFilter', '$scope', '$window', 'currentUser'];
+
+ function ChangePasswordController(ChangePasswordService, UpdateUserService, $filter, trFilter, $scope, $window, currentUser) {
+
+ var vm = this;
+ vm.isOpen = false;
+
+ vm.hasError = false;
+ vm.errorMessage = '';
+
+ vm.reset = reset;
+
+ vm.confirm = confirm;
+ vm.updatePassword = updatePassword;
+ vm.cancel = cancel;
+
+ $scope.user = currentUser.get();
+ if(!$scope.user) {
+ $window.location.href = '/';
+ return;
+ }
+ var userId = $scope.user.user_id;
+
+ //Error message dialog handler for account setting.
+ $scope.$on('modalTitle', function(e, val) {
+ vm.modalTitle = val;
+ });
+
+ $scope.$on('modalMessage', function(e, val) {
+ vm.modalMessage = val;
+ });
+
+ $scope.$on('raiseError', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ $scope.$broadcast('showDialog', false);
+ };
+ vm.contentType = 'text/plain';
+ vm.confirmOnly = true;
+ $scope.$broadcast('showDialog', true);
+ }
+ });
+
+ function reset() {
+ $scope.form.$setUntouched();
+ $scope.form.$setPristine();
+ vm.hasError = false;
+ vm.errorMessage = '';
+ }
+
+ function confirm() {
+ $window.location.href = '/dashboard';
+ }
+
+ function updatePassword(user) {
+ if(user && angular.isDefined(user.oldPassword) && angular.isDefined(user.password)) {
+ vm.action = vm.confirm;
+ ChangePasswordService(userId, user.oldPassword, user.password)
+ .success(changePasswordSuccess)
+ .error(changePasswordFailed);
+ }
+
+ }
+
+ function changePasswordSuccess(data, status) {
+ vm.modalTitle = $filter('tr')('change_password', []);
+ vm.modalMessage = $filter('tr')('successful_changed_password', []);
+ $scope.$broadcast('showDialog', true);
+ }
+
+ function changePasswordFailed(data, status) {
+
+ var message;
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ console.log('Failed to change password:' + data);
+ if(data == 'old_password_is_not_correct') {
+ message = $filter('tr')('old_password_is_incorrect');
+ }else{
+ message = $filter('tr')('failed_to_change_password');
+ }
+
+ $scope.$emit('modalMessage', message);
+ $scope.$emit('raiseError', true);
+ }
+
+ function cancel(form) {
+ $window.location.href = '/dashboard';
+ }
+
+ }
+
+})();
diff --git a/static/resources/js/layout/change-password/change-password.module.js b/static/resources/js/layout/change-password/change-password.module.js
new file mode 100644
index 000000000..ed7e81c6b
--- /dev/null
+++ b/static/resources/js/layout/change-password/change-password.module.js
@@ -0,0 +1,23 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.change.password', [
+ 'harbor.services.user']);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/dashboard/dashboard.controller.js b/static/resources/js/layout/dashboard/dashboard.controller.js
new file mode 100644
index 000000000..bc5de1578
--- /dev/null
+++ b/static/resources/js/layout/dashboard/dashboard.controller.js
@@ -0,0 +1,50 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.dashboard')
+ .controller('DashboardController', DashboardController);
+
+ DashboardController.$inject = ['$scope'];
+
+ function DashboardController($scope) {
+ var vm = this;
+ vm.customBodyHeight = {'height': '165px'};
+
+ //Error message dialog handler for dashboard.
+ $scope.$on('modalTitle', function(e, val) {
+ vm.modalTitle = val;
+ });
+
+ $scope.$on('modalMessage', function(e, val) {
+ vm.modalMessage = val;
+ });
+
+ $scope.$on('raiseError', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ $scope.$broadcast('showDialog', false);
+ };
+ vm.contentType = 'text/plain';
+ vm.confirmOnly = true;
+ $scope.$broadcast('showDialog', true);
+ }
+ });
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/dashboard/dashboard.module.js b/static/resources/js/layout/dashboard/dashboard.module.js
new file mode 100644
index 000000000..f9f24f9bd
--- /dev/null
+++ b/static/resources/js/layout/dashboard/dashboard.module.js
@@ -0,0 +1,24 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.dashboard', [
+ 'harbor.services.repository'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/details/details.config.js b/static/resources/js/layout/details/details.config.js
new file mode 100644
index 000000000..cfcacca29
--- /dev/null
+++ b/static/resources/js/layout/details/details.config.js
@@ -0,0 +1,46 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.details')
+ .filter('name', nameFilter);
+
+ function nameFilter() {
+
+ return filter;
+
+ function filter(input, filterInput, key) {
+ input = input || [];
+ var filteredResults = [];
+
+ if (filterInput !== '') {
+ for(var i = 0; i < input.length; i++) {
+ var item = input[i];
+ if((key === "" && item.indexOf(filterInput) >= 0) || (key !== "" && item[key].indexOf(filterInput) >= 0)) {
+ filteredResults.push(item);
+ continue;
+ }
+ }
+ input = filteredResults;
+ }
+ return input;
+ }
+ }
+
+
+})();
\ 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
new file mode 100644
index 000000000..c891c8e87
--- /dev/null
+++ b/static/resources/js/layout/details/details.controller.js
@@ -0,0 +1,76 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.details')
+ .controller('DetailsController', DetailsController);
+
+ DetailsController.$inject = ['$scope', '$timeout'];
+
+ function DetailsController($scope, $timeout) {
+ var vm = this;
+
+ vm.publicity = false;
+ vm.isProjectMember = false;
+
+ vm.togglePublicity = togglePublicity;
+
+ vm.sectionDefaultHeight = {'min-height': '579px'};
+
+ //Message dialog handler for details.
+ $scope.$on('modalTitle', function(e, val) {
+ vm.modalTitle = val;
+ });
+
+ $scope.$on('modalMessage', function(e, val) {
+ vm.modalMessage = val;
+ });
+
+ $scope.$on('raiseError', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ $scope.$broadcast('showDialog', false);
+ };
+ vm.contentType = 'text/plain';
+ vm.confirmOnly = true;
+
+ $timeout(function() {
+ $scope.$broadcast('showDialog', true);
+ }, 350);
+ }
+ });
+
+ $scope.$on('raiseInfo', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ val.action();
+ $scope.$broadcast('showDialog', false);
+ }
+ vm.contentType = val.contentType;
+ vm.confirmOnly = val.confirmOnly;
+
+ $scope.$broadcast('showDialog', true);
+ }
+ });
+
+ function togglePublicity(e) {
+ vm.publicity = e.publicity;
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/details/details.module.js b/static/resources/js/layout/details/details.module.js
new file mode 100644
index 000000000..e60cd842e
--- /dev/null
+++ b/static/resources/js/layout/details/details.module.js
@@ -0,0 +1,25 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.details', [
+ 'harbor.services.project',
+ 'harbor.services.project.member'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/footer/footer.controller.js b/static/resources/js/layout/footer/footer.controller.js
new file mode 100644
index 000000000..5053c544f
--- /dev/null
+++ b/static/resources/js/layout/footer/footer.controller.js
@@ -0,0 +1,27 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.footer')
+ .controller('FooterController', FooterController);
+
+ function FooterController() {
+ var vm = this;
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/footer/footer.module.js b/static/resources/js/layout/footer/footer.module.js
new file mode 100644
index 000000000..ccdc389d4
--- /dev/null
+++ b/static/resources/js/layout/footer/footer.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.footer', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/forgot-password/forgot-password.controller.js b/static/resources/js/layout/forgot-password/forgot-password.controller.js
new file mode 100644
index 000000000..798fe7e02
--- /dev/null
+++ b/static/resources/js/layout/forgot-password/forgot-password.controller.js
@@ -0,0 +1,103 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.forgot.password')
+ .controller('ForgotPasswordController', ForgotPasswordController);
+
+ ForgotPasswordController.$inject = ['SendMailService', '$window', '$scope', '$filter', 'trFilter'];
+
+ function ForgotPasswordController(SendMailService, $window, $scope, $filter, trFilter) {
+ var vm = this;
+
+ vm.hasError = false;
+ vm.show = false;
+ vm.errorMessage = '';
+
+ vm.reset = reset;
+ vm.sendMail = sendMail;
+
+ vm.confirm = confirm;
+ vm.toggleInProgress = false;
+
+ //Error message dialog handler for forgotting password.
+ $scope.$on('modalTitle', function(e, val) {
+ vm.modalTitle = val;
+ });
+
+ $scope.$on('modalMessage', function(e, val) {
+ vm.modalMessage = val;
+ });
+
+ $scope.$on('raiseError', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ $scope.$broadcast('showDialog', false);
+ };
+ vm.contentType = 'text/plain';
+ vm.confirmOnly = true;
+ $scope.$broadcast('showDialog', true);
+ }
+ });
+
+ function reset(){
+ vm.hasError = false;
+ vm.errorMessage = '';
+ }
+
+ function sendMail(user) {
+ if(user && angular.isDefined(user.email)) {
+
+ vm.action = vm.confirm;
+
+ vm.toggleInProgress = true;
+ SendMailService(user.email)
+ .success(sendMailSuccess)
+ .error(sendMailFailed);
+ }
+ }
+
+ function sendMailSuccess(data, status) {
+ vm.toggleInProgress = false;
+ vm.modalTitle = $filter('tr')('forgot_password');
+ vm.modalMessage = $filter('tr')('mail_has_been_sent');
+ vm.confirmOnly = true;
+ $scope.$broadcast('showDialog', true);
+ }
+
+ function sendMailFailed(data, status) {
+ vm.toggleInProgress = false;
+ vm.hasError = true;
+ vm.errorMessage = data;
+
+ if(status === 500) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_send_email'));
+ $scope.$emit('raiseError', true);
+ }
+ console.log('Failed to send mail:' + data);
+ }
+
+ function confirm() {
+ $window.location.href = '/';
+ }
+
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/forgot-password/forgot-password.module.js b/static/resources/js/layout/forgot-password/forgot-password.module.js
new file mode 100644
index 000000000..7c5ae0dc0
--- /dev/null
+++ b/static/resources/js/layout/forgot-password/forgot-password.module.js
@@ -0,0 +1,24 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.forgot.password', [
+ 'harbor.services.user'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/header/header.controller.js b/static/resources/js/layout/header/header.controller.js
new file mode 100644
index 000000000..869ab9d09
--- /dev/null
+++ b/static/resources/js/layout/header/header.controller.js
@@ -0,0 +1,47 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.header')
+ .controller('HeaderController', HeaderController);
+
+ HeaderController.$inject = ['$scope', '$window', 'getParameterByName', '$location', 'currentUser'];
+
+ function HeaderController($scope, $window, getParameterByName, $location, currentUser) {
+ var vm = this;
+ vm.user = currentUser.get();
+
+ if(location.pathname === '/dashboard') {
+ vm.defaultUrl = '/dashboard';
+ }else{
+ vm.defaultUrl = '/';
+ }
+
+ $scope.$watch('vm.user', function(current) {
+ if(current) {
+ vm.defaultUrl = '/dashboard';
+ }
+ });
+
+ if($window.location.search) {
+ vm.searchInput = getParameterByName('q', $window.location.search);
+ console.log('vm.searchInput at header:' + vm.searchInput);
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/header/header.module.js b/static/resources/js/layout/header/header.module.js
new file mode 100644
index 000000000..b9c84401d
--- /dev/null
+++ b/static/resources/js/layout/header/header.module.js
@@ -0,0 +1,24 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.header', [
+ 'harbor.services.user',
+ 'harbor.services.i18n'
+ ]);
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/index/index.controller.js b/static/resources/js/layout/index/index.controller.js
new file mode 100644
index 000000000..a0dc20eb5
--- /dev/null
+++ b/static/resources/js/layout/index/index.controller.js
@@ -0,0 +1,105 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.index')
+ .controller('IndexController', IndexController);
+
+ IndexController.$inject = ['$scope', '$filter', 'trFilter', '$timeout'];
+
+ function IndexController($scope, $filter, trFilter, $timeout) {
+
+ $scope.subsHeight = 110;
+ $scope.subsSection = 32;
+ $scope.subsSubPane = 226;
+
+ var vm = this;
+
+ vm.customBodyHeight = {'height': '180px'};
+ vm.viewAll = viewAll;
+
+ function viewAll() {
+ var indexDesc = $filter('tr')('index_desc', []);
+ var indexDesc1 = $filter('tr')('index_desc_1', []);
+ var indexDesc2 = $filter('tr')('index_desc_2', []);
+ var indexDesc3 = $filter('tr')('index_desc_3', []);
+ var indexDesc4 = $filter('tr')('index_desc_4', []);
+ var indexDesc5 = $filter('tr')('index_desc_5', []);
+ var indexDesc6 = $filter('tr')('index_desc_6', []);
+
+ $scope.$emit('modalTitle', $filter('tr')('harbor_intro_title'));
+ $scope.$emit('modalMessage', '
'+
+ indexDesc +
+ '
' +
+ '
' +
+ '
▪︎ ' + indexDesc1 + '
' +
+ '
▪︎ ' + indexDesc2 + '
' +
+ '
▪︎ ' + indexDesc3 + '
' +
+ '
▪︎ ' + indexDesc4 + '
' +
+ '
▪︎ ' + indexDesc5 + '
' +
+ '
▪︎ ' + indexDesc6 + '
' +
+ '
');
+ var emitInfo = {
+ 'contentType': 'text/html',
+ 'confirmOnly': true,
+ 'action': function() {
+ $scope.$broadcast('showDialog', false);
+ }
+ };
+ $scope.$emit('raiseInfo', emitInfo);
+ }
+
+ //Message dialog handler for index.
+ $scope.$on('modalTitle', function(e, val) {
+ vm.modalTitle = val;
+ });
+
+ $scope.$on('modalMessage', function(e, val) {
+ vm.modalMessage = val;
+ });
+
+ $scope.$on('raiseError', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ $scope.$broadcast('showDialog', false);
+ };
+ vm.contentType = 'text/plain';
+ vm.confirmOnly = true;
+
+ $timeout(function() {
+ $scope.$broadcast('showDialog', true);
+ }, 350);
+ }
+ });
+
+ $scope.$on('raiseInfo', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ val.action();
+ $scope.$broadcast('showDialog', false);
+ }
+ vm.contentType = val.contentType;
+ vm.confirmOnly = val.confirmOnly;
+
+ $scope.$broadcast('showDialog', true);
+ }
+ });
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/index/index.module.js b/static/resources/js/layout/index/index.module.js
new file mode 100644
index 000000000..167a920fb
--- /dev/null
+++ b/static/resources/js/layout/index/index.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.index', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/navigation/navigation-admin-options.directive.html b/static/resources/js/layout/navigation/navigation-admin-options.directive.html
new file mode 100644
index 000000000..ecbd0fafe
--- /dev/null
+++ b/static/resources/js/layout/navigation/navigation-admin-options.directive.html
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/static/resources/js/layout/navigation/navigation-admin-options.directive.js b/static/resources/js/layout/navigation/navigation-admin-options.directive.js
new file mode 100644
index 000000000..89de7e4a4
--- /dev/null
+++ b/static/resources/js/layout/navigation/navigation-admin-options.directive.js
@@ -0,0 +1,65 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.navigation')
+ .directive('navigationAdminOptions', navigationAdminOptions);
+
+ NavigationAdminOptions.$inject = ['$location'];
+
+ function NavigationAdminOptions($location) {
+ var vm = this;
+ vm.path = $location.path();
+ }
+
+ function navigationAdminOptions() {
+ var directive = {
+ 'restrict': 'E',
+ 'templateUrl': '/static/resources/js/layout/navigation/navigation-admin-options.directive.html',
+ 'scope': {
+ 'target': '='
+ },
+ 'link': link,
+ 'controller': NavigationAdminOptions,
+ 'controllerAs': 'vm',
+ 'bindToController': true
+ };
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+ var visited = ctrl.path.substring(1);
+ console.log('visited:' + visited);
+
+ if(visited) {
+ element.find('a[tag="' + visited + '"]').addClass('active');
+ }else{
+ element.find('a:first').addClass('active');
+ }
+
+ element.find('a').on('click', click);
+
+ function click(event) {
+ element.find('a').removeClass('active');
+ $(event.target).addClass('active');
+ ctrl.target = $(this).attr('tag');
+ scope.$apply();
+ }
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/navigation/navigation-details.directive.js b/static/resources/js/layout/navigation/navigation-details.directive.js
new file mode 100644
index 000000000..bf9b027c7
--- /dev/null
+++ b/static/resources/js/layout/navigation/navigation-details.directive.js
@@ -0,0 +1,80 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.navigation')
+ .directive('navigationDetails', navigationDetails);
+
+ NavigationDetailsController.$inject = ['$window', '$location', '$scope', 'getParameterByName'];
+
+ function NavigationDetailsController($window, $location, $scope, getParameterByName) {
+ var vm = this;
+
+ vm.projectId = getParameterByName('project_id', $location.absUrl());
+
+ $scope.$on('$locationChangeSuccess', function() {
+ vm.projectId = getParameterByName('project_id', $location.absUrl());
+ });
+
+ vm.path = $location.path();
+ }
+
+ function navigationDetails() {
+ var directive = {
+ restrict: 'E',
+ templateUrl: '/navigation_detail?timestamp=' + new Date().getTime(),
+ link: link,
+ scope: {
+ 'target': '='
+ },
+ replace: true,
+ controller: NavigationDetailsController,
+ controllerAs: 'vm',
+ bindToController: true
+ };
+
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+
+ var visited = ctrl.path.substring(1);
+ if(visited.indexOf('?') >= 0) {
+ visited = ctrl.url.substring(1, ctrl.url.indexOf('?'));
+ }
+
+ if(visited) {
+ element.find('a[tag="' + visited + '"]').addClass('active');
+ }else{
+ element.find('a:first').addClass('active');
+ }
+
+ ctrl.target = visited;
+ element.find('a').on('click', click);
+
+ function click(event) {
+ element.find('a').removeClass('active');
+ $(event.target).addClass('active');
+ ctrl.target = $(this).attr('tag');
+ scope.$apply();
+ }
+
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/navigation/navigation-header.directive.js b/static/resources/js/layout/navigation/navigation-header.directive.js
new file mode 100644
index 000000000..d1655f909
--- /dev/null
+++ b/static/resources/js/layout/navigation/navigation-header.directive.js
@@ -0,0 +1,58 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.navigation')
+ .directive('navigationHeader', navigationHeader);
+
+ NavigationHeaderController.$inject = ['$window', '$scope', 'currentUser', '$timeout'];
+
+ function NavigationHeaderController($window, $scope, currentUser, $timeout) {
+ var vm = this;
+ vm.url = $window.location.pathname;
+ }
+
+ function navigationHeader() {
+ var directive = {
+ restrict: 'E',
+ templateUrl: '/navigation_header?timestamp=' + new Date().getTime(),
+ link: link,
+ scope: true,
+ controller: NavigationHeaderController,
+ controllerAs: 'vm',
+ bindToController: true
+ };
+
+ return directive;
+
+ function link(scope, element, attrs, ctrl) {
+ var visited = ctrl.url;
+ console.log('visited:' + visited);
+ if (visited !== '' && visited !== '/') {
+ element.find('a[href*="' + visited + '"]').addClass('active');
+ }
+ element.find('a').on('click', click);
+ function click(event) {
+ element.find('a').removeClass('active');
+ $(event.target).not('span').addClass('active');
+ }
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/navigation/navigation.module.js b/static/resources/js/layout/navigation/navigation.module.js
new file mode 100644
index 000000000..c1154f746
--- /dev/null
+++ b/static/resources/js/layout/navigation/navigation.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.navigation', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/project/project.controller.js b/static/resources/js/layout/project/project.controller.js
new file mode 100644
index 000000000..cb497daf5
--- /dev/null
+++ b/static/resources/js/layout/project/project.controller.js
@@ -0,0 +1,121 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.project')
+ .controller('ProjectController', ProjectController);
+
+ ProjectController.$inject = ['$scope', 'ListProjectService', '$timeout', 'currentUser', 'getRole', '$filter', 'trFilter'];
+
+ function ProjectController($scope, ListProjectService, $timeout, currentUser, getRole, $filter, trFilter) {
+ var vm = this;
+
+ vm.isOpen = false;
+ vm.projectName = '';
+ vm.publicity = 0;
+
+ vm.retrieve = retrieve;
+ vm.showAddProject = showAddProject;
+ vm.searchProject = searchProject;
+ vm.showAddButton = showAddButton;
+ vm.togglePublicity = togglePublicity;
+ vm.user = currentUser.get();
+ vm.retrieve();
+ vm.getProjectRole = getProjectRole;
+
+
+ //Error message dialog handler for project.
+ $scope.$on('modalTitle', function(e, val) {
+ vm.modalTitle = val;
+ });
+
+ $scope.$on('modalMessage', function(e, val) {
+ vm.modalMessage = val;
+ });
+
+ $scope.$on('raiseError', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ $scope.$broadcast('showDialog', false);
+ };
+ vm.contentType = 'text/plain';
+ vm.confirmOnly = true;
+ $scope.$broadcast('showDialog', true);
+ }
+ });
+
+
+ function retrieve() {
+ ListProjectService(vm.projectName, vm.publicity)
+ .success(listProjectSuccess)
+ .error(listProjectFailed);
+ }
+
+ function listProjectSuccess(data, status) {
+ vm.projects = data || [];
+ }
+
+ function getProjectRole(roleId) {
+ if(roleId !== 0) {
+ var role = getRole({'key': 'roleId', 'value': roleId});
+ return role.name;
+ }
+ return '';
+ }
+
+ function listProjectFailed(data, status) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_get_project'));
+ $scope.$emit('raiseError', true);
+ console.log('Failed to get Project.');
+ }
+
+ $scope.$on('addedSuccess', function(e, val) {
+ vm.retrieve();
+ });
+
+ function showAddProject() {
+ if(vm.isOpen){
+ vm.isOpen = false;
+ }else{
+ vm.isOpen = true;
+ }
+ }
+
+ function searchProject() {
+ vm.retrieve();
+ }
+
+ function showAddButton() {
+ if(vm.publicity === 0) {
+ return true;
+ }else{
+ return false;
+ }
+ }
+
+ function togglePublicity(e) {
+ vm.publicity = e.publicity;
+ vm.isOpen = false;
+ vm.retrieve();
+ console.log('vm.publicity:' + vm.publicity);
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/project/project.module.js b/static/resources/js/layout/project/project.module.js
new file mode 100644
index 000000000..82db3c9bb
--- /dev/null
+++ b/static/resources/js/layout/project/project.module.js
@@ -0,0 +1,26 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.project', [
+ 'harbor.project.member',
+ 'harbor.services.project',
+ 'harbor.services.user'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/reset-password/reset-password.controller.js b/static/resources/js/layout/reset-password/reset-password.controller.js
new file mode 100644
index 000000000..3ee85fb3c
--- /dev/null
+++ b/static/resources/js/layout/reset-password/reset-password.controller.js
@@ -0,0 +1,100 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.reset.password')
+ .controller('ResetPasswordController', ResetPasswordController);
+
+ ResetPasswordController.$inject = ['$scope', '$location', 'ResetPasswordService', '$window', 'getParameterByName', '$filter', 'trFilter'];
+
+ function ResetPasswordController($scope, $location, ResetPasswordService, $window, getParameterByName, $filter, trFilter) {
+ var vm = this;
+ vm.resetUuid = getParameterByName('reset_uuid', $location.absUrl());
+
+ vm.reset = reset;
+ vm.resetPassword = resetPassword;
+ vm.confirm = confirm;
+ vm.cancel = cancel;
+
+ vm.hasError = false;
+ vm.errorMessage = '';
+
+ //Error message dialog handler for resetting password.
+ $scope.$on('modalTitle', function(e, val) {
+ vm.modalTitle = val;
+ });
+
+ $scope.$on('modalMessage', function(e, val) {
+ vm.modalMessage = val;
+ });
+
+ $scope.$on('raiseError', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ $scope.$broadcast('showDialog', false);
+ };
+ vm.contentType = 'text/plain';
+ vm.confirmOnly = true;
+ $scope.$broadcast('showDialog', true);
+ }
+ });
+
+ function reset() {
+ vm.hasError = false;
+ vm.errorMessage = '';
+ }
+
+ function resetPassword(user) {
+ if(user && angular.isDefined(user.password)) {
+
+ vm.action = vm.confirm;
+
+ console.log('rececived password:' + user.password + ', reset_uuid:' + vm.resetUuid);
+ ResetPasswordService(vm.resetUuid, user.password)
+ .success(resetPasswordSuccess)
+ .error(resetPasswordFailed);
+ }
+ }
+
+ function confirm() {
+ $window.location.href = '/';
+ }
+
+ function resetPasswordSuccess(data, status) {
+ vm.modalTitle = $filter('tr')('reset_password');
+ vm.modalMessage = $filter('tr')('successful_reset_password');
+ vm.confirmOnly = true;
+ $scope.$broadcast('showDialog', true);
+ }
+
+ function resetPasswordFailed(data) {
+ vm.hasError = true;
+
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_to_reset_pasword') + data);
+ $scope.$emit('raiseError', true);
+
+ console.log('Failed to reset password:' + data);
+ }
+
+ function cancel() {
+ $window.location.href = '/';
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/reset-password/reset-password.module.js b/static/resources/js/layout/reset-password/reset-password.module.js
new file mode 100644
index 000000000..ce10e0bb6
--- /dev/null
+++ b/static/resources/js/layout/reset-password/reset-password.module.js
@@ -0,0 +1,24 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.reset.password', [
+ 'harbor.services.user'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/search/search.controller.js b/static/resources/js/layout/search/search.controller.js
new file mode 100644
index 000000000..37ad1d211
--- /dev/null
+++ b/static/resources/js/layout/search/search.controller.js
@@ -0,0 +1,69 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.search')
+ .controller('SearchController', SearchController);
+
+ SearchController.$inject = ['$location', 'SearchService', '$scope', '$filter', 'trFilter', 'getParameterByName'];
+
+ function SearchController($location, SearchService, $scope, $filter, trFilter, getParameterByName) {
+ var vm = this;
+
+ vm.q = getParameterByName('q', $location.absUrl());
+ console.log('vm.q:' + vm.q);
+ SearchService(vm.q)
+ .success(searchSuccess)
+ .error(searchFailed);
+
+ //Error message dialog handler for search.
+ $scope.$on('modalTitle', function(e, val) {
+ vm.modalTitle = val;
+ });
+
+ $scope.$on('modalMessage', function(e, val) {
+ vm.modalMessage = val;
+ });
+
+ $scope.$on('raiseError', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ $scope.$broadcast('showDialog', false);
+ };
+ vm.contentType = 'text/plain';
+ vm.confirmOnly = true;
+ $scope.$broadcast('showDialog', true);
+ }
+ });
+
+ function searchSuccess(data, status) {
+ vm.repository = data['repository'];
+ vm.project = data['project'];
+ }
+
+ function searchFailed(data, status) {
+
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ $scope.$emit('modalMessage', $filter('tr')('failed_in_search'));
+ $scope.$emit('raiseError', true);
+
+ console.log('Failed to search:' + data);
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/search/search.module.js b/static/resources/js/layout/search/search.module.js
new file mode 100644
index 000000000..0e8f0fb7b
--- /dev/null
+++ b/static/resources/js/layout/search/search.module.js
@@ -0,0 +1,23 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.search', []);
+
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/sign-up/sign-up.controller.js b/static/resources/js/layout/sign-up/sign-up.controller.js
new file mode 100644
index 000000000..f182ee106
--- /dev/null
+++ b/static/resources/js/layout/sign-up/sign-up.controller.js
@@ -0,0 +1,107 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.sign.up')
+ .controller('SignUpController', SignUpController);
+
+ SignUpController.$inject = ['$scope', 'SignUpService', '$window', '$filter', 'trFilter'];
+
+ function SignUpController($scope, SignUpService, $window, $filter, trFilter) {
+ var vm = this;
+
+ vm.user = {};
+ vm.signUp = signUp;
+ vm.confirm = confirm;
+
+ //Error message dialog handler for signing up.
+ $scope.$on('modalTitle', function(e, val) {
+ vm.modalTitle = val;
+ });
+
+ $scope.$on('modalMessage', function(e, val) {
+ vm.modalMessage = val;
+ });
+
+ $scope.$on('raiseError', function(e, val) {
+ if(val) {
+ vm.action = function() {
+ $scope.$broadcast('showDialog', false);
+ };
+ vm.contentType = 'text/plain';
+ vm.confirmOnly = true;
+ $scope.$broadcast('showDialog', true);
+ }
+ });
+
+ function signUp(user) {
+ var userObject = {
+ 'username': user.username,
+ 'email': user.email,
+ 'password': user.password,
+ 'realname': user.fullName,
+ 'comment': user.comment
+ };
+
+ vm.action = vm.confirm;
+
+ SignUpService(userObject)
+ .success(signUpSuccess)
+ .error(signUpFailed);
+ }
+
+ function signUpSuccess(data, status) {
+ var title;
+ var message;
+ if(vm.targetType) {
+ title = $filter('tr')('add_new_title');
+ message = $filter('tr')('successful_added');
+ }else{
+ title = $filter('tr')('sign_up');
+ message = $filter('tr')('successful_signed_up');
+ }
+ vm.modalTitle = title;
+ vm.modalMessage = message;
+ $scope.$broadcast('showDialog', true);
+ }
+
+ function signUpFailed(data, status) {
+ $scope.$emit('modalTitle', $filter('tr')('error'));
+ var message;
+ if(vm.targetType) {
+ message = $filter('tr')('failed_to_add_user');
+ }else{
+ message = $filter('tr')('failed_to_sign_up');
+ }
+ $scope.$emit('modalMessage', message);
+ $scope.$emit('raiseError', true);
+
+ console.log('Signed up failed.');
+ }
+
+ function confirm() {
+ if(location.pathname === '/add_new') {
+ $window.location.href = '/dashboard';
+ }else{
+ $window.location.href = '/';
+ }
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/layout/sign-up/sign-up.module.js b/static/resources/js/layout/sign-up/sign-up.module.js
new file mode 100644
index 000000000..c038e7789
--- /dev/null
+++ b/static/resources/js/layout/sign-up/sign-up.module.js
@@ -0,0 +1,23 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.sign.up', [
+ 'harbor.services.user']);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/project.js b/static/resources/js/project.js
deleted file mode 100644
index 427aa39dd..000000000
--- a/static/resources/js/project.js
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- Copyright (c) 2016 VMware, Inc. All Rights Reserved.
- Licensed 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.
-*/
-jQuery(function(){
-
- new AjaxUtil({
- url: "/api/users/current",
- type: "get",
- success: function(data, status, xhr){
- if(xhr && xhr.status == 200){
- if(data.has_admin_role == 1) {
- renderForAdminRole();
- }
- renderForAnyRole();
- }
- }
- }).exec();
-
- function renderForAnyRole(){
- $("#tabProject a:first").tab("show");
-
- $(document).on("keydown", function(e){
- if(e.keyCode == 13){
- e.preventDefault();
- if($("#tabProject li:eq(0)").is(":focus") || $("#txtSearchProject").is(":focus")){
- $("#btnSearch").trigger("click");
- }else if($("#tabProject li:eq(1)").is(":focus") || $("#txtSearchPublicProjects").is(":focus")){
- $("#btnSearchPublicProjects").trigger("click");
- }else if($("#tabProject li:eq(1)").is(":focus") || $("#txtSearchUsername").is(":focus")){
- $("#btnSearchUsername").trigger("click");
- }else if($("#dlgAddProject").is(":focus") || $("#projectName").is(":focus")){
- $("#btnSave").trigger("click");
- }
- }
- });
-
- function listProject(projectName, isPublic){
- currentPublic = isPublic;
- $.when(
- new AjaxUtil({
- url: "/api/projects?is_public=" + isPublic + "&project_name=" + (projectName == null ? "" : projectName) + "×tamp=" + new Date().getTime(),
- type: "get",
- success: function(data, status, xhr){
- $("#tblProject tbody tr").remove();
- $.each(data || [], function(i, e){
- var row = '
');
- }
- });
- }
-});
\ No newline at end of file
diff --git a/static/resources/js/services/destination/services.create-destination.js b/static/resources/js/services/destination/services.create-destination.js
new file mode 100644
index 000000000..8a2c1f3ad
--- /dev/null
+++ b/static/resources/js/services/destination/services.create-destination.js
@@ -0,0 +1,38 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.destination')
+ .factory('CreateDestinationService', CreateDestinationService);
+
+ CreateDestinationService.$inject = ['$http'];
+
+ function CreateDestinationService($http) {
+ return createDestination;
+ function createDestination(name, endpoint, username, password) {
+ return $http
+ .post('/api/targets', {
+ 'name': name,
+ 'endpoint': endpoint,
+ 'username': username,
+ 'password': password
+ });
+ }
+ }
+
+})()
\ No newline at end of file
diff --git a/static/resources/js/services/destination/services.delete-destination.js b/static/resources/js/services/destination/services.delete-destination.js
new file mode 100644
index 000000000..6a34585da
--- /dev/null
+++ b/static/resources/js/services/destination/services.delete-destination.js
@@ -0,0 +1,33 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.destination')
+ .factory('DeleteDestinationService', DeleteDestinationService);
+
+ DeleteDestinationService.$inject = ['$http'];
+
+ function DeleteDestinationService($http) {
+ return deleteDestination;
+ function deleteDestination(targetId) {
+ return $http
+ .delete('/api/targets/' + targetId);
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/destination/services.destination.module.js b/static/resources/js/services/destination/services.destination.module.js
new file mode 100644
index 000000000..919d47734
--- /dev/null
+++ b/static/resources/js/services/destination/services.destination.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.destination', []);
+
+})();
diff --git a/static/resources/js/services/destination/services.list-destination-policy.js b/static/resources/js/services/destination/services.list-destination-policy.js
new file mode 100644
index 000000000..9aae0445c
--- /dev/null
+++ b/static/resources/js/services/destination/services.list-destination-policy.js
@@ -0,0 +1,33 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.destination')
+ .factory('ListDestinationPolicyService', ListDestinationPolicyService);
+
+ ListDestinationPolicyService.$inject = ['$http'];
+
+ function ListDestinationPolicyService($http) {
+ return listDestinationPolicy;
+ function listDestinationPolicy(targetId) {
+ return $http
+ .get('/api/targets/' + targetId + '/policies/');
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/destination/services.list-destination.js b/static/resources/js/services/destination/services.list-destination.js
new file mode 100644
index 000000000..eb8a3f7c0
--- /dev/null
+++ b/static/resources/js/services/destination/services.list-destination.js
@@ -0,0 +1,37 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.destination')
+ .factory('ListDestinationService', ListDestinationService);
+
+ ListDestinationService.$inject = ['$http'];
+
+ function ListDestinationService($http) {
+ return listDestination;
+ function listDestination(targetId, name) {
+ return $http
+ .get('/api/targets/' + targetId, {
+ 'params': {
+ 'name': name
+ }
+ });
+ }
+ }
+
+})()
\ No newline at end of file
diff --git a/static/resources/js/services/destination/services.ping-destination.js b/static/resources/js/services/destination/services.ping-destination.js
new file mode 100644
index 000000000..58a103f66
--- /dev/null
+++ b/static/resources/js/services/destination/services.ping-destination.js
@@ -0,0 +1,57 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.destination')
+ .factory('PingDestinationService', PingDestinationService);
+
+ PingDestinationService.$inject = ['$http'];
+
+ function PingDestinationService($http) {
+ return pingDestination;
+ function pingDestination(target) {
+ var payload = {};
+ if(target['id']) {
+ payload = {'id': target['id']};
+ }else {
+ payload = {
+ 'name': target['name'],
+ 'endpoint': target['endpoint'],
+ 'username': target['username'],
+ 'password': target['password']
+ };
+ }
+
+ return $http({
+ 'method': 'POST',
+ 'url': '/api/targets/ping',
+ 'headers': {'Content-Type': 'application/x-www-form-urlencoded'},
+ 'transformRequest': function(obj) {
+ var str = [];
+ for(var p in obj) {
+ str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
+ }
+ return str.join("&");
+ },
+ 'timeout': 30000,
+ 'data': payload
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/destination/services.update-destination.js b/static/resources/js/services/destination/services.update-destination.js
new file mode 100644
index 000000000..1791f4349
--- /dev/null
+++ b/static/resources/js/services/destination/services.update-destination.js
@@ -0,0 +1,38 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.destination')
+ .factory('UpdateDestinationService', UpdateDestinationService);
+
+ UpdateDestinationService.$inject = ['$http'];
+
+ function UpdateDestinationService($http) {
+ return updateDestination;
+ function updateDestination(targetId, target) {
+ return $http
+ .put('/api/targets/' + targetId, {
+ 'name': target.name,
+ 'endpoint': target.endpoint,
+ 'username': target.username,
+ 'password': target.password
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/i18n/locale_messages_en-US.js b/static/resources/js/services/i18n/locale_messages_en-US.js
new file mode 100644
index 000000000..68793ec7d
--- /dev/null
+++ b/static/resources/js/services/i18n/locale_messages_en-US.js
@@ -0,0 +1,270 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+var locale_messages = {
+ 'sign_in': 'Sign In',
+ 'sign_up': 'Sign Up',
+ 'forgot_password': 'Forgot Password',
+ 'login_now': 'Login Now',
+ 'its_easy_to_get_started': 'It\'s easy to get started ...',
+ 'icon_label_1': 'Anonymous repository access',
+ 'icon_label_2': 'Repositories managed by project',
+ 'icon_label_3': 'Role based access control',
+ 'why_use_harbor': 'Why use Harbor?',
+ 'index_desc': 'Project Harbor is an enterprise-class registry server, which extends the open source Docker Registry server by adding the functionality usually required by an enterprise, such as security, control, and management. Harbor is primarily designed to be a private registry - providing the needed security and control that enterprises require. It also helps minimize bandwidth usage, which is helpful to both improve productivity as well as performance.',
+ 'index_desc_1': 'Security: Keep their intellectual properties within their organizations.',
+ 'index_desc_2': 'Efficiency: A private registry server is set up within the organization\'s network and can reduce significantly the internet traffic to the public service. ',
+ 'index_desc_3': 'Access Control: RBAC (Role Based Access Control) is provided. User management can be integrated with existing enterprise identity services like AD/LDAP. ',
+ 'index_desc_4': 'Audit: All access to the registry are logged and can be used for audit purpose.',
+ 'index_desc_5': 'GUI: User friendly single-pane-of-glass management console.',
+ 'index_desc_6': 'Image Replication: Replicate images between instances.',
+ 'view_all': 'View all...',
+ 'repositories': 'Repositories',
+ 'project_repo_name': 'Project/Repository Name',
+ 'creation_time': 'Creation Time',
+ 'author': 'Author',
+ 'username': 'Username',
+ 'username_is_required': 'Username is required.',
+ 'username_has_been_taken': 'Username has been taken.',
+ 'username_is_too_long': 'Username is too long. (maximum 20 characters)',
+ 'username_contains_illegal_chars': 'Username contains illegal character(s).',
+ 'email': 'Email',
+ 'email_desc': 'The Email address will be used for resetting password.',
+ 'email_is_required': 'Email is required.',
+ 'email_has_been_taken': 'Email has been taken.',
+ 'email_content_illegal': 'Email format is illegal.',
+ 'email_does_not_exist': 'Email does not exist.',
+ 'email_is_too_long': 'Email is to long. (maximum 50 characters)',
+ 'full_name': 'Full Name',
+ 'full_name_desc': 'First name & Last name',
+ 'full_name_is_required': 'Full name is required.',
+ 'full_name_is_too_long': 'Full name is too long. (maximum 20 characters)',
+ 'full_name_contains_illegal_chars': 'Full name contains illegal character(s).',
+ 'password': 'Password',
+ 'password_desc': 'At least 7 characters with 1 lowercase letter, 1 capital letter and 1 numeric character.',
+ 'password_is_required': 'Password is required.',
+ 'password_is_invalid': 'Password is invalid. At least 7 characters with 1 lowercase letter, 1 capital letter and 1 numeric character.',
+ 'confirm_password': 'Confirm Password',
+ 'password_does_not_match': 'Passwords do not match.',
+ 'comments': 'Comments',
+ 'comment_is_too_long': 'Comment is too long. (maximum 20 characters)',
+ 'forgot_password_description': 'Please input the Email used when you signed up, a reset password Email will be sent to you.',
+ 'reset_password': 'Reset Password',
+ 'successful_reset_password': 'Password has been reset successfully.',
+ 'failed_to_change_password': 'Failed to change password.',
+ 'summary': 'Summary',
+ 'projects': 'Projects',
+ 'public_projects': 'Public Projects',
+ 'public': 'Public',
+ 'public_repositories': 'Public Repositories',
+ 'my_project_count': 'My Projects',
+ 'my_repo_count': 'My Repositories',
+ 'public_project_count': 'Public Projects',
+ 'public_repo_count': 'Public Repositories',
+ 'total_project_count': 'Total Projects',
+ 'total_repo_count': 'Total Repositories',
+ 'top_10_repositories': 'Top 10 Repositories',
+ 'repository_name': 'Repository Name',
+ 'size': 'Size',
+ 'count': 'Downloads',
+ 'creator': 'Creator',
+ 'no_top_repositories': 'No data, start with Harbor now!',
+ 'logs': 'Logs',
+ 'task_name': 'Task Name',
+ 'details': 'Details',
+ 'user': 'User',
+ 'no_user_logs': 'No data, start with Harbor now!',
+ 'users': 'Users',
+ 'my_projects': 'My Projects',
+ 'project_name': 'Project Name',
+ 'role': 'Role',
+ 'publicity': 'Publicity',
+ 'button_on': 'On',
+ 'button_off': 'Off',
+ 'new_project': 'New Project',
+ 'save': 'Save',
+ 'cancel': 'Cancel',
+ 'confirm': 'Confirm',
+ 'items': 'item(s)',
+ 'add_member': 'Add Member',
+ 'operation': 'Operation',
+ 'advanced_search': 'Advanced Search',
+ 'all': 'All',
+ 'others': 'Others',
+ 'search': 'Search',
+ 'duration': 'Duration',
+ 'from': 'From',
+ 'to': 'To',
+ 'timestamp': 'Timestamp',
+ 'dashboard': 'Dashboard',
+ 'admin_options': 'Admin Options',
+ 'account_setting': 'Account Settings',
+ 'log_out': 'Log Out',
+ 'registration_time': 'Registration Time',
+ 'system_management': 'System Management',
+ 'change_password': 'Change Password',
+ 'search_result': 'Search Result',
+ 'old_password': 'Old Password',
+ 'old_password_is_required': 'Old password is required.',
+ 'old_password_is_incorrect': 'Old password is incorrect.',
+ 'new_password_is_required': 'New password is required.',
+ 'new_password_is_invalid': 'New password is invalid. At least 7 characters with 1 lowercase letter, 1 capital letter and 1 numeric character.',
+ 'new_password': 'New Password',
+ 'username_already_exist': 'Username already exist.',
+ 'username_does_not_exist': 'Username does not exist.',
+ 'username_or_password_is_incorrect': 'Username or password is incorrect',
+ 'username_email': 'Username/Email',
+ 'project_name_is_required': 'Project name is required',
+ 'project_already_exist': 'Project already exist',
+ 'project_name_is_invalid': 'Project name is invalid, it should be all lowercase and with no space.',
+ 'project_name_is_too_short': 'Project name is too short, it should be greater than 4 characters.',
+ 'project_name_is_too_long': 'Project name is too long, it should be less than 30 characters.',
+ 'search_projects_or_repositories': 'Search projects or repositories',
+ 'tag': 'Tag',
+ 'image_details': 'Image Details',
+ 'pull_command': 'Pull Command',
+ 'alert_delete_repo_title': 'Confirm Deletion',
+ 'alert_delete_repo': 'All tags under this repository will be deleted. ' +
+ 'The space of this repository will be recycled during garbage collection. ' +
+ ' Delete repository "$0" now?',
+ 'alert_delete_tag_title': 'Confirm Deletion',
+ 'alert_delete_tag': 'Delete tag "$0" now?',
+ 'close': 'Close',
+ 'ok': 'OK',
+ 'welcome': 'Welcome to Harbor!',
+ 'continue' : 'Continue',
+ 'no_projects_add_new_project': 'No projects available now.',
+ 'no_repositories': 'No repositories found, please use "docker push" to upload images.',
+ 'failed_to_add_member': 'Project member can not be added, insuffient permissions.',
+ 'failed_to_change_member': 'Project member can not be changed, insuffient permissions.',
+ 'failed_to_delete_member': 'Project member can not be deleted, insuffient permissions.',
+ 'confirm_delete_user_title': 'User Deletion',
+ 'confirm_delete_user': 'Are you sure to delete the user "$0" ?',
+ 'confirm_delete_destination_title': 'Destination Deletion',
+ 'confirm_delete_destination': 'Are you sure to delete the destination "$0" ?',
+ 'replication': 'Replication',
+ 'name': 'Name',
+ 'description': 'Description',
+ 'destination': 'Destination',
+ 'start_time': 'Start Time',
+ 'last_start_time': 'Last Start Time',
+ 'end_time': 'End Time',
+ 'activation': 'Activation',
+ 'replication_jobs': 'Replication Jobs',
+ 'actions': 'Actions',
+ 'status': 'Status',
+ 'logs' : 'Logs',
+ 'enabled': 'Enabled',
+ 'enable': 'Enable',
+ 'disabled': 'Disabled',
+ 'disable': 'Disable',
+ 'no_replication_policies_add_new': 'No replication policies, please add new policy.',
+ 'no_replication_policies': 'No replication policies.',
+ 'no_replication_jobs': 'No replication jobs.',
+ 'no_destinations': 'No destinations, please add new destination.',
+ 'name_is_required': 'Name is required.',
+ 'name_is_too_long': 'Name is too long. (maximum 20 characters)',
+ 'description_is_too_long': 'Description is too long. ',
+ 'general_setting': 'General',
+ 'destination_setting': 'Destination Settings',
+ 'endpoint': 'Endpoint',
+ 'endpoint_is_required': 'Endpoint is required.',
+ 'test_connection': 'Test connection',
+ 'add_new_destination': 'New Destination',
+ 'edit_destination': 'Edit Destination',
+ 'successful_changed_password': 'Password has been changed successfully.',
+ 'change_profile': 'Change Profile',
+ 'successful_changed_profile': 'User profile has been changed successfully.',
+ 'administrator': 'Administrator',
+ 'popular_repositories': 'Popular Repositories',
+ 'harbor_intro_title': 'About Harbor',
+ 'mail_has_been_sent': 'Password resetting Email has been sent.',
+ 'send': 'Send',
+ 'successful_signed_up': 'Signed up successfully.',
+ 'add_new_policy': 'Add New Policy',
+ 'edit_policy': 'Edit Policy',
+ 'add_new_title': 'Add User',
+ 'add_new': 'Add',
+ 'successful_added': 'New user added successfully.',
+ 'copyright': 'Copyright',
+ 'all_rights_reserved': 'All Rights Reserved.',
+ 'pinging_target': 'Testing connection ...',
+ 'successful_ping_target': 'Connection tested successfully.',
+ 'failed_to_ping_target': 'Connetion test failed, please check your settings.',
+ 'policy_already_exists': 'Policy already exists.',
+ 'destination_already_exists': 'Destination already exists.',
+ 'refresh': 'Refresh',
+ 'select_all': 'Select All',
+ 'delete_tag': 'Delete Tag',
+ 'delete_repo': 'Delete Repo',
+ 'download_log': 'View Logs',
+ 'edit': 'Edit',
+ 'delete': 'Delete',
+ 'transfer': 'Transfer',
+ 'all': 'All',
+ 'pending': 'Pending',
+ 'running': 'Running',
+ '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. ',
+ 'failed_to_delete_repo_insuffient_permissions': 'Failed to delete repository, insuffient permissions.',
+ 'failed_to_get_tag': 'Failed to get tag.',
+ 'failed_to_get_log': 'Failed to get logs.',
+ 'failed_to_get_project': 'Failed to get projects.',
+ 'failed_to_update_user': 'Failed to update user.',
+ 'failed_to_get_stat': 'Failed to get stat data.',
+ 'failed_to_get_top_repo': 'Failed to get top repositories.',
+ 'failed_to_get_user_log': 'Failed to get user logs.',
+ 'failed_to_send_email': 'Failed to send email.',
+ 'failed_to_reset_pasword': 'Failed to reset password.',
+ 'failed_in_search': 'Failed in search.',
+ 'failed_to_sign_up': 'Failed to sign up.',
+ 'failed_to_add_user': 'Failed to add user.',
+ 'failed_to_delete_user': 'Failed to delete user.',
+ 'failed_to_list_user': 'Failed to list user data.',
+ 'failed_to_toggle_admin': 'Failed to toggle admin user.',
+ 'failed_to_list_destination': 'Failed to list destinations.',
+ 'failed_to_list_replication': 'Failed to list replication policies.',
+ 'failed_to_toggle_policy': 'Failed to toggle replication policy.',
+ 'failed_to_create_replication_policy': 'Failed to create replication policy.',
+ 'failed_to_get_destination': 'Failed to get destination.',
+ 'failed_to_get_destination_policies': 'Failed to get destination policies.',
+ 'failed_to_get_replication_policy': 'Failed to get replication policy.',
+ 'failed_to_update_replication_policy': 'Failed to update replication policy.',
+ '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',
+ 'inline_help_role_title': 'The Definitions of Roles',
+ 'inline_help_role': 'Project Admin: Project Admin has read/write and member management privileges to the project. ' +
+ 'Developer: Developer has read and write privileges to the project. ' +
+ 'Guest: Guest has read-only privilege for a specified project.',
+ 'inline_help_publicity_title': 'Publicity of Project',
+ 'inline_help_publicity': 'When a project is set to public, anyone will have read permission to the repositories under this project, and user will not need to run "docker login" before pulling images under this project.',
+ 'alert_job_contains_error': 'Found errors in the current replication jobs, please check.',
+ 'found_error_in_replication_job': 'Found $0 error(s).',
+ 'caution': 'Caution',
+ 'confirm_to_toggle_enabled_policy_title': 'Enable Policy',
+ 'confirm_to_toggle_enabled_policy': 'After enabling the policy, replication jobs will be triggered to replicate all repositories under the project to the destination of the policy. Please confirm to continue.',
+ 'confirm_to_toggle_disabled_policy_title': 'Disable Policy',
+ 'confirm_to_toggle_disabled_policy': 'After disabling the policy, running replication jobs of this policy will be canceled. Please confirm to continue.'
+};
diff --git a/static/resources/js/services/i18n/locale_messages_zh-CN.js b/static/resources/js/services/i18n/locale_messages_zh-CN.js
new file mode 100644
index 000000000..98bc435bb
--- /dev/null
+++ b/static/resources/js/services/i18n/locale_messages_zh-CN.js
@@ -0,0 +1,272 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+var locale_messages = {
+ 'sign_in': '登录',
+ 'sign_up': '注册',
+ 'forgot_password': '忘记密码',
+ 'login_now': '登录',
+ 'its_easy_to_get_started': '这很容易上手...',
+ 'icon_label_1': '匿名访问公开镜像仓库',
+ 'icon_label_2': '基于项目的镜像管理',
+ 'icon_label_3': '用户角色访问控制',
+ 'why_use_harbor': '为什么要使用Harbor?',
+ 'index_desc': 'Harbor是可靠的企业级Registry服务器。企业用户可使用Harbor搭建私有容器Registry服务,提高生产效率和安全度,既可应用于生产环境,也可以在开发环境中使用。',
+ 'index_desc_1': '安全: 确保知识产权在自己组织内部的管控之下。',
+ 'index_desc_2': '效率: 搭建组织内部的私有容器Registry服务,可显著降低访问公共Registry服务的网络需求。',
+ 'index_desc_3': '访问控制: 提供基于角色的访问控制,可集成企业目前拥有的用户管理系统(如:AD/LDAP)。',
+ 'index_desc_4': '审计: 所有访问Registry服务的操作均被记录,便于日后审计。',
+ 'index_desc_5': '管理界面: 具有友好易用图形管理界面。',
+ 'index_desc_6': '镜像复制: 在实例之间复制镜像。',
+ 'view_all': '显示全部...',
+ 'repositories': '镜像仓库',
+ 'project_repo_name': '项目/镜像仓库名称',
+ 'creation_time': '创建时间',
+ 'author': '作者',
+ 'username': '用户名',
+ 'username_is_required' : '用户名为必填项。',
+ 'username_has_been_taken' : '用户名已被占用。',
+ 'username_is_too_long' : '用户名长度超出限制。(最长为20个字符)',
+ 'username_contains_illegal_chars': '用户名包含不合法的字符。',
+ 'email': '邮箱',
+ 'email_desc': '此邮箱将用于重置密码。',
+ 'email_is_required' : '邮箱为必填项。',
+ 'email_has_been_taken' : '邮箱已被占用。',
+ 'email_content_illegal' : '邮箱格式不合法。',
+ 'email_does_not_exist' : '邮箱不存在。',
+ 'email_is_too_long': '邮箱名称长度超出限制。(最长为50个字符)',
+ 'full_name': '全名',
+ 'full_name_desc': '请输入全名。',
+ 'full_name_is_required' : '全名为必填项。',
+ 'full_name_is_too_long' : '全名长度超出限制。(最长为20个字符)',
+ 'full_name_contains_illegal_chars' : '全名包含不合法的字符。',
+ 'password': '密码',
+ 'password_desc': '至少输入 7个字符且包含 1个小写字母, 1个大写字母和 1个数字。',
+ 'password_is_required' : '密码为必填项。',
+ 'password_is_invalid' : '密码无效。至少输入 7个字符且包含 1个小写字母,1个大写字母和 1个数字。',
+ 'confirm_password': '确认密码',
+ 'password_does_not_match' : '两次密码输入不一致。',
+ 'comments': '备注',
+ 'comment_is_too_long' : '备注长度超出限制。(最长为20个字符)',
+ 'forgot_password_description': '重置邮件将发送到此邮箱。',
+ 'reset_password': '重置密码',
+ 'successful_reset_password': '重置密码成功。',
+ 'failed_to_change_password': '修改密码失败。',
+ 'summary': '摘要',
+ 'projects': '项目',
+ 'public_projects': '公开项目',
+ 'public': '公开',
+ 'public_repositories': '公开镜像仓库',
+ 'my_project_count': '我的项目',
+ 'my_repo_count': '我的镜像仓库',
+ 'public_project_count': '公开项目',
+ 'public_repo_count': '公开镜像仓库',
+ 'total_project_count': '全部项目',
+ 'total_repo_count': '全部镜像仓库',
+ 'top_10_repositories': 'Top 10 镜像仓库',
+ 'repository_name': '镜像仓库名',
+ 'size': '大小',
+ 'count': '下载次数',
+ 'creator': '创建者',
+ 'no_top_repositories': '暂时没有数据。',
+ 'logs': '日志',
+ 'task_name': '任务名称',
+ 'details': '详细信息',
+ 'user': '用户',
+ 'no_user_logs': '暂时没有数据。',
+ 'users': '用户',
+ 'my_projects': '我的项目',
+ 'project_name': '项目名称',
+ 'role': '角色',
+ 'publicity': '公开',
+ 'button_on': '是',
+ 'button_off': '否',
+ 'new_project': '新增项目',
+ 'save': '保存',
+ 'cancel': '取消',
+ 'confirm': '确认',
+ 'items': '条记录',
+ 'add_member': '新增成员',
+ 'operation': '操作',
+ 'advanced_search': '高级搜索',
+ 'all': '全部',
+ 'others': '其他',
+ 'search':'搜索',
+ 'duration': '持续时间',
+ 'from': '起始',
+ 'to': '结束',
+ 'timestamp': '时间戳',
+ 'dashboard': '控制面板',
+ 'admin_options': '管理员选项',
+ 'account_setting': '账户设置',
+ 'log_out': '注销',
+ 'registration_time': '注册时间',
+ 'system_management': '系统管理',
+ 'change_password': '修改密码',
+ 'search_result': '搜索结果',
+ 'old_password': '原密码',
+ 'old_password_is_required': '原密码为必填项。',
+ 'old_password_is_incorrect': '原密码不正确。',
+ 'new_password_is_required': '新密码为必填项。',
+ 'new_password_is_invalid': '新密码无效。至少输入 7个字符且包含 1个小写字母,1个大写字母和 1个数字。',
+ 'new_password': '新密码',
+ 'username_already_exist': '用户名已存在。',
+ 'username_does_not_exist': '用户名不存在。',
+ 'username_or_password_is_incorrect': '用户名或密码不正确。',
+ 'username_email': '用户名/邮箱',
+ 'project_name_is_required': '项目名称为必填项。',
+ 'project_already_exist': '项目已存在。',
+ 'project_name_is_invalid': '项目名称无效。全部为小写字母,且不能包含空格。',
+ 'project_name_is_too_short': '项目名称长度过短,至少多于4个字符。',
+ 'project_name_is_too_long': '项目名称长度超出限制,最长30个字符。',
+ 'search_projects_or_repositories': '搜索项目和镜像资源',
+ 'tag': '标签',
+ 'image_details': '镜像明细',
+ 'pull_command': 'Pull 命令',
+ 'alert_delete_repo_title': '确认删除',
+ 'alert_delete_repo': '即将删除镜像仓库下的所有标签,镜像空间将在垃圾回收过程中释放。 ' +
+ ' 是否删除镜像仓库 "$0" ?',
+ 'alert_delete_tag_title': '确认删除',
+ 'alert_delete_tag': '删除镜像标签 "$0" ?',
+ 'close': '关闭',
+ 'ok': '确认',
+ 'welcome': '欢迎使用Harbor!',
+ 'continue' : '继续',
+ 'no_projects_add_new_project': '当前没有项目。',
+ 'no_repositories': '未发现镜像,请用"docker push"命令上传镜像。',
+ 'failed_to_add_member': '无法添加项目成员,权限不足。',
+ 'failed_to_change_member': '无法修改项目成员,权限不足。',
+ 'failed_to_delete_member': '无法删除项目成员,权限不足。',
+ 'confirm_delete_user_title': '删除用户',
+ 'confirm_delete_user': '确认删除用户 "$0" ?',
+ 'confirm_delete_destination_title': '删除目标',
+ 'confirm_delete_destination': '确认删除目标 "$0"?',
+ 'replication': '复制',
+ 'name': '名称',
+ 'description': '描述',
+ 'destination': '目标',
+ 'start_time': '起始时间',
+ 'last_start_time': '上次起始时间',
+ 'end_time': '结束时间',
+ 'activation': '活动状态',
+ 'replication_jobs': '复制任务',
+ 'actions': '操作',
+ 'status': '状态',
+ 'logs': '日志',
+ 'enabled': '已启用',
+ 'enable': '启用',
+ 'disabled': '已停用',
+ 'disable': '停用',
+ 'no_replication_policies_add_new': '没有复制策略,请新增复制策略。',
+ 'no_replication_policies': '没有复制策略。',
+ 'no_replications': '没有复制策略。',
+ 'no_replication_jobs': '没有复制任务。',
+ 'no_destinations': '没有目标设置,请新增目标。',
+ 'name_is_required': '名称为必填项',
+ 'name_is_too_long': '名称长度超出限制。(最长为20个字符)',
+ 'description_is_too_long': '描述内容长度超出限制。',
+ 'general_setting': '一般设置',
+ 'destination_setting': '目标设置',
+ 'endpoint': '终端URL',
+ 'endpoint_is_required': '终端URL为必填项。',
+ 'test_connection': '测试连接',
+ 'add_new_destination': '新建目标',
+ 'edit_destination': '编辑目标',
+ 'successful_changed_password': '修改密码操作成功。',
+ 'change_profile': '修改账户信息',
+ 'successful_changed_profile': '修改账户信息操作成功。',
+ 'administrator': '管理员',
+ 'popular_repositories': '热门镜像仓库',
+ 'harbor_intro_title': '关于 Harbor',
+ 'mail_has_been_sent': '重置密码邮件已发送。',
+ 'send': '发送',
+ 'successful_signed_up': '注册成功。',
+ 'add_new_policy': '新增策略',
+ 'edit_policy': '修改策略',
+ 'add_new_title': '新增用户',
+ 'add_new': '新增',
+ 'successful_added': '新增用户成功。',
+ 'copyright': '版权所有',
+ 'all_rights_reserved': '保留所有权利。',
+ 'pinging_target': '正在测试连接……',
+ 'successful_ping_target': '测试连接目标成功。',
+ 'failed_to_ping_target': '测试连接目标失败,请检查设置。',
+ 'policy_already_exists': '策略已存在。',
+ 'destination_already_exists': '目标已存在。',
+ 'refresh': '刷新',
+ 'select_all': '全选',
+ 'delete_tag': '删除镜像标签',
+ 'delete_repo': '删除镜像仓库',
+ 'download_log': '查看日志',
+ 'edit': '修改',
+ 'delete': '删除',
+ 'all': '全部',
+ 'transfer': '复制',
+ 'pending': '等待中',
+ 'running': '进行中',
+ 'finished': '已完成',
+ 'canceled': '已取消',
+ 'stopped': '已终止',
+ 'retrying': '重试中',
+ 'error': '错误',
+ 'failed_to_get_project_member': '无法获取当前项目成员。',
+ 'failed_to_delete_repo': '无法删除镜像仓库。',
+ 'failed_to_delete_repo_insuffient_permissions': '无法删除镜像仓库,权限不足。',
+ 'failed_to_get_tag': '获取标签数据失败。',
+ 'failed_to_get_log': '获取日志数据失败。',
+ 'failed_to_get_project': '获取项目数据失败。',
+ 'failed_to_update_user': '更新用户信息失败。',
+ 'failed_to_get_stat': '获取统计数据失败。',
+ 'failed_to_get_top_repo': '获取热门镜像仓库数据失败。',
+ 'failed_to_get_user_log': '获取用户日志数据失败。',
+ 'failed_to_send_email': '发送邮件失败。',
+ 'failed_to_reset_pasword': '重置邮件失败。',
+ 'failed_in_search': '搜索操作失败。',
+ 'failed_to_sign_up': '注册用户失败。',
+ 'failed_to_add_user': '新增用户失败。',
+ 'failed_to_delete_user': '删除用户失败。',
+ 'failed_to_list_user': '获取用户数据失败。',
+ 'failed_to_toggle_admin': '切换管理员用户失败。',
+ 'failed_to_list_destination': '获取目标数据失败。',
+ 'failed_to_list_replication': '获取复制策略数据失败。',
+ 'failed_to_toggle_policy': '切换复制策略状态失败。',
+ 'failed_to_create_replication_policy': '创建复制策略失败。',
+ 'failed_to_get_destination': '获取目标失败。',
+ 'failed_to_get_destination_policies': '获取目标关联策略数据失败。',
+ 'failed_to_get_replication_policy': '获取复制策略失败。',
+ 'failed_to_update_replication_policy': '修改复制策略失败。',
+ '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': '访客',
+ 'inline_help_role_title': '角色定义',
+ 'inline_help_role': '项目管理员: “项目管理员”拥有一个项目的读/写和成员管理的权限。 '+
+ '开发人员: “开发人员” 拥有一个项目的读/写权限。 ' +
+ '访客: “访客”拥有特定项目的只读权限。',
+ 'inline_help_publicity_title': '公开项目',
+ 'inline_help_publicity': '当项目设为公开后,任何人都有此项目下镜像的读权限。命令行用户不需要“docker login”就可以拉取此项目下的镜像。',
+ 'alert_job_contains_error': '当前复制任务中包含错误,请检查。',
+ 'found_error_in_replication_job': '发现 $0 个错误。',
+ 'caution': '注意',
+ 'confirm_to_toggle_policy_title': '切换复制策略状态',
+ 'confirm_to_toggle_policy': '确认将复制策略 $0 切换为 $1 状态吗?',
+ 'confirm_to_toggle_enabled_policy_title': '启用策略',
+ 'confirm_to_toggle_enabled_policy': '启用策略后,系统将触发复制任务来同步项目下的所有镜像仓库到策略的目标实例。请确认继续。',
+ 'confirm_to_toggle_disabled_policy_title': '停用策略',
+ 'confirm_to_toggle_disabled_policy': '停用策略后,属于此策略的所有复制任务将终止。请确认继续。'
+};
diff --git a/static/resources/js/services/i18n/services.i18n.js b/static/resources/js/services/i18n/services.i18n.js
new file mode 100644
index 000000000..1f77a7c4f
--- /dev/null
+++ b/static/resources/js/services/i18n/services.i18n.js
@@ -0,0 +1,82 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.i18n')
+ .factory('I18nService', I18nService);
+
+ I18nService.$inject = ['$cookies', '$window'];
+
+ function I18nService($cookies, $window) {
+
+ var cookieOptions = {'path': '/'};
+
+ var messages = $.extend(true, {}, eval('locale_messages'));
+ var defaultLanguage = 'en-US';
+ var supportLanguages = {
+ 'en-US': 'English',
+ 'zh-CN': '中文'
+ };
+ var isSupportLanguage = function(language) {
+ for (var i in supportLanguages) {
+ if(language === String(i)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+
+ return tr;
+ function tr() {
+
+ return {
+ 'setCurrentLanguage': function(language) {
+ if(!angular.isDefined(language) || !isSupportLanguage(language)) {
+ language = defaultLanguage;
+ }
+ $cookies.put('language', language, cookieOptions);
+ },
+ 'setDefaultLanguage': function() {
+ $cookies.put('language', defaultLanguage, cookieOptions);
+ },
+ 'getCurrentLanguage': function() {
+ return $cookies.get('language') || defaultLanguage;
+ },
+ 'getLanguageName': function(language) {
+ if(!angular.isDefined(language) || !isSupportLanguage(language)) {
+ language = defaultLanguage;
+ }
+ $cookies.put('language', language, cookieOptions);
+ return supportLanguages[language];
+ },
+ 'getSupportLanguages': function() {
+ return supportLanguages;
+ },
+ 'unset': function(){
+ $cookies.put('language', defaultLanguage, cookieOptions);
+ },
+ 'getValue': function(key) {
+ return messages[key];
+ }
+ };
+
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/i18n/services.i18n.module.js b/static/resources/js/services/i18n/services.i18n.module.js
new file mode 100644
index 000000000..ddaa9f266
--- /dev/null
+++ b/static/resources/js/services/i18n/services.i18n.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.i18n', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/log/services.list-integrated-log.js b/static/resources/js/services/log/services.list-integrated-log.js
new file mode 100644
index 000000000..206bb63b7
--- /dev/null
+++ b/static/resources/js/services/log/services.list-integrated-log.js
@@ -0,0 +1,40 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.services.log')
+ .factory('ListIntegratedLogService', ListIntegratedLogService);
+
+ ListIntegratedLogService.$inject = ['$http', '$log'];
+
+ function ListIntegratedLogService($http, $log) {
+
+ return listIntegratedLog;
+
+ function listIntegratedLog(lines) {
+ $log.info('Get recent logs of the projects which the user is a member of:');
+ return $http
+ .get('/api/logs', {
+ 'params' : {
+ 'lines': lines,
+ }
+ });
+
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/log/services.list-log.js b/static/resources/js/services/log/services.list-log.js
new file mode 100644
index 000000000..732c36c50
--- /dev/null
+++ b/static/resources/js/services/log/services.list-log.js
@@ -0,0 +1,46 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.log')
+ .factory('ListLogService', ListLogService);
+
+ ListLogService.$inject = ['$http', '$log'];
+
+ function ListLogService($http, $log) {
+
+ return LogResult;
+
+ function LogResult(queryParams) {
+ var projectId = queryParams.projectId;
+ var username = queryParams.username;
+ var beginTimestamp = queryParams.beginTimestamp;
+ var endTimestamp = queryParams.endTimestamp;
+ var keywords = queryParams.keywords;
+
+ return $http
+ .post('/api/projects/' + projectId + '/logs/filter', {
+ 'begin_timestamp' : beginTimestamp,
+ 'end_timestamp' : endTimestamp,
+ 'keywords' : keywords,
+ 'project_id': Number(projectId),
+ 'username' : username
+ });
+ }
+ }
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/log/services.log.module.js b/static/resources/js/services/log/services.log.module.js
new file mode 100644
index 000000000..d07727291
--- /dev/null
+++ b/static/resources/js/services/log/services.log.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.log', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project-member/services.add-project-member.js b/static/resources/js/services/project-member/services.add-project-member.js
new file mode 100644
index 000000000..a25b9ebfd
--- /dev/null
+++ b/static/resources/js/services/project-member/services.add-project-member.js
@@ -0,0 +1,39 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.project.member')
+ .factory('AddProjectMemberService', AddProjectMemberService);
+
+ AddProjectMemberService.$inject = ['$http', '$log'];
+
+ function AddProjectMemberService($http, $log) {
+
+ return AddProjectMember;
+
+ function AddProjectMember(projectId, roles, username) {
+ return $http
+ .post('/api/projects/' + projectId + '/members/', {
+ 'roles': [ Number(roles) ],
+ 'username': username
+ });
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project-member/services.current-project-member.js b/static/resources/js/services/project-member/services.current-project-member.js
new file mode 100644
index 000000000..1be51ed92
--- /dev/null
+++ b/static/resources/js/services/project-member/services.current-project-member.js
@@ -0,0 +1,34 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.project.member')
+ .factory('CurrentProjectMemberService', CurrentProjectMemberService);
+
+ CurrentProjectMemberService.$inject = ['$http', '$log'];
+
+ function CurrentProjectMemberService($http, $log) {
+ return currentProjectMember;
+
+ function currentProjectMember(projectId) {
+ return $http
+ .get('/api/projects/' + projectId + '/members/current');
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project-member/services.delete-project-member.js b/static/resources/js/services/project-member/services.delete-project-member.js
new file mode 100644
index 000000000..a1df3da11
--- /dev/null
+++ b/static/resources/js/services/project-member/services.delete-project-member.js
@@ -0,0 +1,36 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.project.member')
+ .factory('DeleteProjectMemberService', DeleteProjectMemberService);
+
+ DeleteProjectMemberService.$inject = ['$http', '$log'];
+
+ function DeleteProjectMemberService($http, $log) {
+
+ return DeleteProjectMember;
+
+ function DeleteProjectMember(projectId, userId) {
+ return $http
+ .delete('/api/projects/' + projectId + '/members/' + userId);
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project-member/services.edit-project-member.js b/static/resources/js/services/project-member/services.edit-project-member.js
new file mode 100644
index 000000000..c84a239c1
--- /dev/null
+++ b/static/resources/js/services/project-member/services.edit-project-member.js
@@ -0,0 +1,38 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.project.member')
+ .factory('EditProjectMemberService', EditProjectMemberService);
+
+ EditProjectMemberService.$inject = ['$http', '$log'];
+
+ function EditProjectMemberService($http, $log) {
+
+ return EditProjectMember;
+
+ function EditProjectMember(projectId, userId, roleId) {
+ return $http
+ .put('/api/projects/' + projectId + '/members/' + userId, {
+ 'roles' : [ Number(roleId) ]
+ });
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project-member/services.list-project-member.js b/static/resources/js/services/project-member/services.list-project-member.js
new file mode 100644
index 000000000..5e15d8753
--- /dev/null
+++ b/static/resources/js/services/project-member/services.list-project-member.js
@@ -0,0 +1,41 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.project.member')
+ .service('ListProjectMemberService', ListProjectMemberService);
+
+ ListProjectMemberService.$inject = ['$http', '$log'];
+
+ function ListProjectMemberService($http, $log) {
+
+ return ListProjectMember;
+
+ function ListProjectMember(projectId, queryParams) {
+ console.log('project_member project_id:' + projectId);
+ var username = queryParams.username;
+ return $http
+ .get('/api/projects/' + projectId + '/members', {
+ params: {
+ 'username': username
+ }
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project-member/services.project-member.module.js b/static/resources/js/services/project-member/services.project-member.module.js
new file mode 100644
index 000000000..e2e4bb413
--- /dev/null
+++ b/static/resources/js/services/project-member/services.project-member.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.project.member', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project/services.add-project.js b/static/resources/js/services/project/services.add-project.js
new file mode 100644
index 000000000..cb1986daa
--- /dev/null
+++ b/static/resources/js/services/project/services.add-project.js
@@ -0,0 +1,37 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.services.project')
+ .factory('AddProjectService', AddProjectService);
+
+ AddProjectService.$inject = ['$http', '$log'];
+
+ function AddProjectService($http, $log) {
+
+ return AddProject;
+
+ function AddProject(projectName, isPublic) {
+ return $http
+ .post('/api/projects', {
+ 'project_name': projectName,
+ 'public': isPublic
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project/services.edit-project.js b/static/resources/js/services/project/services.edit-project.js
new file mode 100644
index 000000000..cd5e3584e
--- /dev/null
+++ b/static/resources/js/services/project/services.edit-project.js
@@ -0,0 +1,36 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.services.project')
+ .factory('EditProjectService', EditProjectService);
+
+ EditProjectService.$inject = ['$http', '$log'];
+
+ function EditProjectService($http, $log) {
+
+ return EditProject;
+
+ function EditProject(projectId, isPublic) {
+ return $http
+ .put('/api/projects/' + projectId, {
+ 'public': isPublic
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project/services.get-project-by-id.js b/static/resources/js/services/project/services.get-project-by-id.js
new file mode 100644
index 000000000..feb0ef215
--- /dev/null
+++ b/static/resources/js/services/project/services.get-project-by-id.js
@@ -0,0 +1,36 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.project')
+ .factory('GetProjectById', GetProjectById);
+
+ GetProjectById.$inject = ['$http'];
+
+ function GetProjectById($http) {
+
+ return getProject;
+
+ function getProject(id) {
+ return $http
+ .get('/api/projects/' + id);
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project/services.list-project.js b/static/resources/js/services/project/services.list-project.js
new file mode 100644
index 000000000..91d5d5dbe
--- /dev/null
+++ b/static/resources/js/services/project/services.list-project.js
@@ -0,0 +1,40 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.services.project')
+ .factory('ListProjectService', ListProjectService);
+
+ ListProjectService.$inject = ['$http', '$log'];
+
+ function ListProjectService($http, $log) {
+
+ return ListProject;
+
+ function ListProject(projectName, isPublic) {
+ $log.info('list project projectName:' + projectName, ', isPublic:' + isPublic);
+ return $http
+ .get('/api/projects', {
+ 'params' : {
+ 'is_public': isPublic,
+ 'project_name': projectName
+ }
+ });
+
+ }
+ }
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project/services.project.module.js b/static/resources/js/services/project/services.project.module.js
new file mode 100644
index 000000000..f9a3c54a3
--- /dev/null
+++ b/static/resources/js/services/project/services.project.module.js
@@ -0,0 +1,21 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.services.project', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project/services.stat-project.js b/static/resources/js/services/project/services.stat-project.js
new file mode 100644
index 000000000..e471a162f
--- /dev/null
+++ b/static/resources/js/services/project/services.stat-project.js
@@ -0,0 +1,37 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.project')
+ .factory('StatProjectService', StatProjectService);
+
+ StatProjectService.$inject = ['$http', '$log'];
+
+ function StatProjectService($http, $log) {
+
+ return StatProject;
+
+ function StatProject() {
+ $log.info('statistics projects and repositories');
+ return $http
+ .get('/api/statistics');
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/project/services.toggle-project-publicity.js b/static/resources/js/services/project/services.toggle-project-publicity.js
new file mode 100644
index 000000000..dfb9f5ccb
--- /dev/null
+++ b/static/resources/js/services/project/services.toggle-project-publicity.js
@@ -0,0 +1,36 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.project')
+ .factory('ToggleProjectPublicityService', ToggleProjectPublicityService);
+
+ ToggleProjectPublicityService.$inject = ['$http'];
+
+ function ToggleProjectPublicityService($http) {
+ return toggleProjectPublicity;
+ function toggleProjectPublicity(projectId, isPublic) {
+ return $http
+ .put('/api/projects/' + projectId + '/publicity', {
+ 'public': isPublic
+ });
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/replication-job/services.list-replication-job.js b/static/resources/js/services/replication-job/services.list-replication-job.js
new file mode 100644
index 000000000..231da6608
--- /dev/null
+++ b/static/resources/js/services/replication-job/services.list-replication-job.js
@@ -0,0 +1,42 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.replication.job')
+ .factory('ListReplicationJobService', ListReplicationJobService);
+
+ ListReplicationJobService.$inject = ['$http'];
+
+ function ListReplicationJobService($http) {
+
+ return listReplicationJob;
+
+ function listReplicationJob(policyId, repository, status, startTime, endTime) {
+ return $http
+ .get('/api/jobs/replication/', {
+ 'params': {
+ 'policy_id': policyId,
+ 'repository': repository,
+ 'status': status,
+ 'start_time': startTime,
+ 'end_time': endTime
+ }
+ });
+ }
+ }
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/replication-job/services.replication-job.module.js b/static/resources/js/services/replication-job/services.replication-job.module.js
new file mode 100644
index 000000000..9b78fcd45
--- /dev/null
+++ b/static/resources/js/services/replication-job/services.replication-job.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.replication.job', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/replication-policy/services.create-replication-policy.js b/static/resources/js/services/replication-policy/services.create-replication-policy.js
new file mode 100644
index 000000000..96e7c1c77
--- /dev/null
+++ b/static/resources/js/services/replication-policy/services.create-replication-policy.js
@@ -0,0 +1,42 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.replication.policy')
+ .factory('CreateReplicationPolicyService', CreateReplicationPolicyService);
+
+ CreateReplicationPolicyService.$inject = ['$http'];
+
+ function CreateReplicationPolicyService($http) {
+ return createReplicationPolicy;
+
+ function createReplicationPolicy(policy) {
+ return $http
+ .post('/api/policies/replication', {
+ 'project_id': policy.projectId,
+ 'target_id': policy.targetId,
+ 'name': policy.name,
+ 'enabled': policy.enabled,
+ 'description': policy.description,
+ 'cron_str': policy.cronStr,
+ 'start_time': policy.startTime
+ })
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/replication-policy/services.list-replication-policy.js b/static/resources/js/services/replication-policy/services.list-replication-policy.js
new file mode 100644
index 000000000..dc93aa18b
--- /dev/null
+++ b/static/resources/js/services/replication-policy/services.list-replication-policy.js
@@ -0,0 +1,41 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.replication.policy')
+ .factory('ListReplicationPolicyService', ListReplicationPolicyService);
+
+ ListReplicationPolicyService.$inject = ['$http'];
+
+ function ListReplicationPolicyService($http) {
+
+ return listReplicationPolicy;
+
+ function listReplicationPolicy(policyId, projectId, name) {
+ return $http
+ .get('/api/policies/replication/' + policyId, {
+ 'params': {
+ 'project_id': projectId,
+ 'name': name
+ }
+ });
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/replication-policy/services.replication-policy.module.js b/static/resources/js/services/replication-policy/services.replication-policy.module.js
new file mode 100644
index 000000000..f01f674b2
--- /dev/null
+++ b/static/resources/js/services/replication-policy/services.replication-policy.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.replication.policy', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/replication-policy/services.toggle-replication-policy.js b/static/resources/js/services/replication-policy/services.toggle-replication-policy.js
new file mode 100644
index 000000000..635aa4f6f
--- /dev/null
+++ b/static/resources/js/services/replication-policy/services.toggle-replication-policy.js
@@ -0,0 +1,35 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.replication.policy')
+ .factory('ToggleReplicationPolicyService', ToggleReplicationPolicyService);
+
+ ToggleReplicationPolicyService.$inject = ['$http'];
+
+ function ToggleReplicationPolicyService($http) {
+ return toggleReplicationPolicy;
+ function toggleReplicationPolicy(policyId, enabled) {
+ return $http
+ .put('/api/policies/replication/' + policyId + '/enablement', {
+ 'enabled': enabled
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/replication-policy/services.update-replication-policy.js b/static/resources/js/services/replication-policy/services.update-replication-policy.js
new file mode 100644
index 000000000..fb49aeeac
--- /dev/null
+++ b/static/resources/js/services/replication-policy/services.update-replication-policy.js
@@ -0,0 +1,38 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.replication.policy')
+ .factory('UpdateReplicationPolicyService', UpdateReplicationPolicyService);
+
+ UpdateReplicationPolicyService.$inject = ['$http'];
+
+ function UpdateReplicationPolicyService($http) {
+ return updateReplicationPolicy;
+ function updateReplicationPolicy(policyId, policy) {
+ return $http
+ .put('/api/policies/replication/' + policyId, {
+ 'name': policy.name,
+ 'description': policy.description,
+ 'enabled': policy.enabled,
+ 'target_id': policy.targetId
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/repository/services.delete-repository.js b/static/resources/js/services/repository/services.delete-repository.js
new file mode 100644
index 000000000..7d2e39ef0
--- /dev/null
+++ b/static/resources/js/services/repository/services.delete-repository.js
@@ -0,0 +1,36 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.services.repository')
+ .factory('DeleteRepositoryService', DeleteRepositoryService);
+
+ DeleteRepositoryService.$inject = ['$http', '$log'];
+
+ function DeleteRepositoryService($http, $log) {
+
+ return DeleteRepository;
+
+ function DeleteRepository(repoName, tag) {
+ var params = (tag === '') ? {'repo_name' : repoName} : {'repo_name': repoName, 'tag': tag};
+ return $http
+ .delete('/api/repositories', {
+ 'params': params
+ });
+ }
+ }
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/repository/services.list-manifest.js b/static/resources/js/services/repository/services.list-manifest.js
new file mode 100644
index 000000000..b94d70c0c
--- /dev/null
+++ b/static/resources/js/services/repository/services.list-manifest.js
@@ -0,0 +1,38 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.repository')
+ .factory('ListManifestService', ListManifestService);
+
+ ListManifestService.$inject = ['$http', '$log'];
+
+ function ListManifestService($http, $log) {
+ return ListManifest;
+ function ListManifest(repoName, tag) {
+ return $http
+ .get('/api/repositories/manifests', {
+ 'params': {
+ 'repo_name': repoName,
+ 'tag': tag
+ }
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/repository/services.list-repository.js b/static/resources/js/services/repository/services.list-repository.js
new file mode 100644
index 000000000..5cdb5cc6b
--- /dev/null
+++ b/static/resources/js/services/repository/services.list-repository.js
@@ -0,0 +1,40 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.services.repository')
+ .factory('ListRepositoryService', ListRepositoryService);
+
+ ListRepositoryService.$inject = ['$http', '$log'];
+
+ function ListRepositoryService($http, $log) {
+
+ return ListRepository;
+
+ function ListRepository(projectId, q) {
+ $log.info('list repositories:' + projectId + ', q:' + q);
+
+ return $http
+ .get('/api/repositories', {
+ 'params':{
+ 'project_id': projectId,
+ 'q': q
+ }
+ });
+ }
+ }
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/repository/services.list-tag.js b/static/resources/js/services/repository/services.list-tag.js
new file mode 100644
index 000000000..74abd5026
--- /dev/null
+++ b/static/resources/js/services/repository/services.list-tag.js
@@ -0,0 +1,39 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.repository')
+ .factory('ListTagService', ListTagService);
+
+ ListTagService.$inject = ['$http', '$log'];
+
+ function ListTagService($http, $log) {
+ return ListTag;
+
+ function ListTag(repoName) {
+ return $http
+ .get('/api/repositories/tags', {
+ 'params': {
+ 'repo_name': repoName
+ }
+ });
+ }
+ }
+
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/repository/services.list-top-repository.js b/static/resources/js/services/repository/services.list-top-repository.js
new file mode 100644
index 000000000..6cd7ba97d
--- /dev/null
+++ b/static/resources/js/services/repository/services.list-top-repository.js
@@ -0,0 +1,40 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.services.repository')
+ .factory('ListTopRepositoryService', ListTopRepositoryService);
+
+ ListTopRepositoryService.$inject = ['$http', '$log'];
+
+ function ListTopRepositoryService($http, $log) {
+
+ return listTopRepository;
+
+ function listTopRepository(count) {
+ $log.info('Get public repositories which are accessed most:');
+ return $http
+ .get('/api/repositories/top', {
+ 'params' : {
+ 'count': count,
+ }
+ });
+
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/repository/services.repository.module.js b/static/resources/js/services/repository/services.repository.module.js
new file mode 100644
index 000000000..4a638d38c
--- /dev/null
+++ b/static/resources/js/services/repository/services.repository.module.js
@@ -0,0 +1,21 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+ 'use strict';
+
+ angular
+ .module('harbor.services.repository', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/search/services.search.js b/static/resources/js/services/search/services.search.js
new file mode 100644
index 000000000..432310eea
--- /dev/null
+++ b/static/resources/js/services/search/services.search.js
@@ -0,0 +1,40 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.search')
+ .factory('SearchService', SearchService);
+
+ SearchService.$inject = ['$http', '$log'];
+
+ function SearchService($http, $log) {
+
+ return search;
+
+ function search(keywords) {
+ return $http
+ .get('/api/search',{
+ params: {
+ 'q': keywords
+ }
+ });
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/search/services.search.module.js b/static/resources/js/services/search/services.search.module.js
new file mode 100644
index 000000000..d2dbb4680
--- /dev/null
+++ b/static/resources/js/services/search/services.search.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.search', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.change-password.js b/static/resources/js/services/user/services.change-password.js
new file mode 100644
index 000000000..21fc8868c
--- /dev/null
+++ b/static/resources/js/services/user/services.change-password.js
@@ -0,0 +1,39 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('ChangePasswordService', ChangePasswordService);
+
+ ChangePasswordService.$inject = ['$http', '$log'];
+
+ function ChangePasswordService($http, $log) {
+
+ return ChangePassword;
+
+ function ChangePassword(userId, oldPassword, newPassword) {
+ return $http
+ .put('/api/users/' + userId + '/password', {
+ 'old_password': oldPassword,
+ 'new_password': newPassword
+ });
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.current-user.js b/static/resources/js/services/user/services.current-user.js
new file mode 100644
index 000000000..577f53dca
--- /dev/null
+++ b/static/resources/js/services/user/services.current-user.js
@@ -0,0 +1,34 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('CurrentUserService', CurrentUserService);
+
+ CurrentUserService.$inject = ['$http'];
+
+ function CurrentUserService($http, $log) {
+
+ return CurrentUser;
+
+ function CurrentUser() {
+ return $http
+ .get('/api/users/current');
+ }
+ }
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.delete-user.js b/static/resources/js/services/user/services.delete-user.js
new file mode 100644
index 000000000..85d5dd43a
--- /dev/null
+++ b/static/resources/js/services/user/services.delete-user.js
@@ -0,0 +1,36 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('DeleteUserService', DeleteUserService);
+
+ DeleteUserService.$inject = ['$http', '$log'];
+
+ function DeleteUserService($http, $log) {
+
+ return DeleteUser;
+
+ function DeleteUser(userId) {
+ return $http
+ .delete('/api/users/' + userId);
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.is-admin.js b/static/resources/js/services/user/services.is-admin.js
new file mode 100644
index 000000000..8375f2f14
--- /dev/null
+++ b/static/resources/js/services/user/services.is-admin.js
@@ -0,0 +1,35 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('IsAdminService', IsAdminService);
+
+ IsAdminService.$inject = ['$http', '$log'];
+
+ function IsAdminService($http, $log) {
+
+ return IsAdmin;
+
+ function IsAdmin() {
+
+ }
+
+ }
+
+})
\ No newline at end of file
diff --git a/static/resources/js/header-login.js b/static/resources/js/services/user/services.list-user.js
similarity index 58%
rename from static/resources/js/header-login.js
rename to static/resources/js/services/user/services.list-user.js
index 1bd5cda0d..fda39c897 100644
--- a/static/resources/js/header-login.js
+++ b/static/resources/js/services/user/services.list-user.js
@@ -12,21 +12,27 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
-jQuery(function(){
- $("#btnSignUp").css({"visibility": "visible"});
-
- $(document).on("keydown", function(e){
- if(e.keyCode == 13){
- e.preventDefault();
- if($("#txtCommonSearch").is(":focus")){
- document.location = "/search?q=" + $("#txtCommonSearch").val();
- }
- }
- });
- $("#btnSignIn").on("click", function(){
- document.location = "/signIn";
- });
- $("#btnSignUp").on("click", function(){
- document.location = "/register";
- });
-});
\ No newline at end of file
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('ListUserService', ListUserService);
+
+ ListUserService.$inject = ['$http', '$log'];
+
+ function ListUserService($http, $log) {
+
+ return listUser;
+
+ function listUser(username) {
+ return $http
+ .get('/api/users', {
+ 'params' : {
+ 'username': username
+ }
+ });
+ }
+ }
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.log-out.js b/static/resources/js/services/user/services.log-out.js
new file mode 100644
index 000000000..69bfb4dd3
--- /dev/null
+++ b/static/resources/js/services/user/services.log-out.js
@@ -0,0 +1,33 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('LogOutService', LogOutService);
+
+ LogOutService.$inject = ['$http'];
+
+ function LogOutService($http) {
+ return logOut;
+ function logOut() {
+ return $http
+ .get('/log_out');
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.reset-password.js b/static/resources/js/services/user/services.reset-password.js
new file mode 100644
index 000000000..638f8eaa3
--- /dev/null
+++ b/static/resources/js/services/user/services.reset-password.js
@@ -0,0 +1,44 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('ResetPasswordService', ResetPasswordService);
+
+ ResetPasswordService.$inject = ['$http', '$log'];
+
+ function ResetPasswordService($http, $log) {
+ return resetPassword;
+ function resetPassword(uuid, password) {
+ return $http({
+ method: 'POST',
+ url: '/reset',
+ headers: {'Content-Type': 'application/x-www-form-urlencoded'},
+ transformRequest: function(obj) {
+ var str = [];
+ for(var p in obj) {
+ str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
+ }
+ return str.join("&");
+ },
+ data: {'reset_uuid': uuid, 'password': password}
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.send-mail.js b/static/resources/js/services/user/services.send-mail.js
new file mode 100644
index 000000000..dee323131
--- /dev/null
+++ b/static/resources/js/services/user/services.send-mail.js
@@ -0,0 +1,40 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('SendMailService', SendMailService);
+
+ SendMailService.$inject = ['$http', '$log'];
+
+ function SendMailService($http, $log) {
+
+ return SendMail;
+
+ function SendMail(email) {
+ return $http
+ .get('/sendEmail', {
+ 'params': {
+ 'email': email
+ }
+ });
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.sign-in.js b/static/resources/js/services/user/services.sign-in.js
new file mode 100644
index 000000000..05010cc4f
--- /dev/null
+++ b/static/resources/js/services/user/services.sign-in.js
@@ -0,0 +1,45 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('SignInService', SignInService);
+
+ SignInService.$inject = ['$http', '$log'];
+
+ function SignInService($http, $log) {
+
+ return SignIn;
+
+ function SignIn(principal, password) {
+ return $http({
+ method: 'POST',
+ url: '/login',
+ headers: {'Content-Type': 'application/x-www-form-urlencoded'},
+ transformRequest: function(obj) {
+ var str = [];
+ for(var p in obj) {
+ str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
+ }
+ return str.join("&");
+ },
+ data: {'principal': principal, 'password': password}
+ });
+ }
+ }
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.sign-up.js b/static/resources/js/services/user/services.sign-up.js
new file mode 100644
index 000000000..099e18f45
--- /dev/null
+++ b/static/resources/js/services/user/services.sign-up.js
@@ -0,0 +1,40 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('SignUpService', SignUpService);
+
+ SignUpService.$inject = ['$http', '$log'];
+
+ function SignUpService($http, $log) {
+
+ return SignUp;
+
+ function SignUp(user) {
+ return $http
+ .post('/api/users', {
+ 'username': user.username,
+ 'email': user.email,
+ 'password': user.password,
+ 'realname': user.realname,
+ 'comment': user.comment
+ });
+ }
+ }
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.toggle-admin.js b/static/resources/js/services/user/services.toggle-admin.js
new file mode 100644
index 000000000..287d7e9ec
--- /dev/null
+++ b/static/resources/js/services/user/services.toggle-admin.js
@@ -0,0 +1,38 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('ToggleAdminService', ToggleAdminService);
+
+ ToggleAdminService.$inject = ['$http'];
+
+ function ToggleAdminService($http) {
+
+ return toggleAdmin;
+
+ function toggleAdmin(userId, enabled) {
+ return $http
+ .put('/api/users/' + userId + '/sysadmin', {
+ 'has_admin_role' : enabled
+ });
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.update-user.js b/static/resources/js/services/user/services.update-user.js
new file mode 100644
index 000000000..97892f0b1
--- /dev/null
+++ b/static/resources/js/services/user/services.update-user.js
@@ -0,0 +1,38 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('UpdateUserService', UpdateUserService);
+
+ UpdateUserService.$inject = ['$http'];
+
+ function UpdateUserService($http) {
+ return updateUser;
+ function updateUser(userId, user) {
+ return $http
+ .put('/api/users/' + userId, {
+ 'username': user.username,
+ 'email': user.email,
+ 'realname': user.realname,
+ 'comment': user.comment
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.user-exist.js b/static/resources/js/services/user/services.user-exist.js
new file mode 100644
index 000000000..ba8a503e3
--- /dev/null
+++ b/static/resources/js/services/user/services.user-exist.js
@@ -0,0 +1,37 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('UserExistService', UserExistService);
+
+ UserExistService.$inject = ['$http', '$log'];
+
+ function UserExistService($http, $log) {
+ return userExist;
+ function userExist(target, value) {
+ return $.ajax({
+ type: 'POST',
+ url: '/userExists',
+ async: false,
+ data: {'target': target, 'value': value}
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/services/user/services.user.module.js b/static/resources/js/services/user/services.user.module.js
new file mode 100644
index 000000000..36a9b9278
--- /dev/null
+++ b/static/resources/js/services/user/services.user.module.js
@@ -0,0 +1,22 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user', []);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/session/session.current-user.js b/static/resources/js/session/session.current-user.js
new file mode 100644
index 000000000..9e51af4cd
--- /dev/null
+++ b/static/resources/js/session/session.current-user.js
@@ -0,0 +1,47 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.session')
+ .controller('CurrentUserController', CurrentUserController);
+
+ CurrentUserController.$inject = ['$scope', 'CurrentUserService', 'currentUser', '$window', '$document'];
+
+ function CurrentUserController($scope, CurrentUserService, currentUser, $window, $document) {
+
+ var vm = this;
+
+ CurrentUserService()
+ .then(getCurrentUserComplete)
+ .catch(getCurrentUserFailed);
+
+ function getCurrentUserComplete(response) {
+ if(angular.isDefined(response)) {
+ currentUser.set(response.data);
+ if(location.pathname === '/') {
+ $window.location.href = '/dashboard';
+ }
+ }
+ }
+
+ function getCurrentUserFailed(e){
+ console.log('No session of current user.');
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/session/session.module.js b/static/resources/js/session/session.module.js
new file mode 100644
index 000000000..8158793bd
--- /dev/null
+++ b/static/resources/js/session/session.module.js
@@ -0,0 +1,24 @@
+/*
+ Copyright (c) 2016 VMware, Inc. All Rights Reserved.
+ Licensed 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.
+*/
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.session', [
+ 'harbor.services.user'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/resources/js/sign-in.js b/static/resources/js/sign-in.js
deleted file mode 100644
index 2aaa24c5f..000000000
--- a/static/resources/js/sign-in.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- Copyright (c) 2016 VMware, Inc. All Rights Reserved.
- Licensed 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.
-*/
-jQuery(function(){
-
- new AjaxUtil({
- url: "/api/users/current",
- type: "get",
- success: function(data, status, xhr){
- if(xhr && xhr.status == 200){
- document.location = "/registry/project";
- }
- },
- error: function(jqxhr){
- return false;
- }
- }).exec();
-
- $(document).on("keydown", function(e){
- if(e.keyCode == 13){
- e.preventDefault();
- if($("#Principal").is(":focus") || $("#Password").is(":focus") || $("#btnPageSignIn").is(":focus")){
- $("#btnPageSignIn").trigger("click");
- }
- }
- });
- $("#btnForgot").on("click", function(){
- document.location = "/forgotPassword";
- });
-
- $("#btnPageSignIn").on("click", function(){
-
- var principal = $.trim($("#Principal").val());
- var password = $.trim($("#Password").val());
-
- if($.trim(principal).length <= 0 || $.trim(password).length <= 0) {
- $("#dlgModal").dialogModal({"title": i18n.getMessage("title_login_failed"), "content": i18n.getMessage("input_your_username_and_password")});
- return;
- }
-
- $.ajax({
- url:'/login',
- data: {principal: principal, password: password},
- type: "post",
- success: function(jqXhr, status){
- var lastUri = location.search;
- if(lastUri != "" && lastUri.indexOf("=") > 0){
- document.location = decodeURIComponent(lastUri.split("=")[1]);
- }else{
- document.location = "/registry/project";
- }
- },
- error: function(jqXhr){
- var i18nKey = "";
- if(jqXhr.status == 500){
- i18nKey = "internal_error";
- }else{
- i18nKey = "check_your_username_or_password"
- }
- $("#dlgModal")
- .dialogModal({
- "title": i18n.getMessage("title_login_failed"),
- "content": i18n.getMessage(i18nKey)
- });
- }
- });
- });
-});
\ No newline at end of file
diff --git a/static/resources/js/validate-options.js b/static/resources/js/validate-options.js
deleted file mode 100644
index 492366ca7..000000000
--- a/static/resources/js/validate-options.js
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- Copyright (c) 2016 VMware, Inc. All Rights Reserved.
- Licensed 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.
-*/
-var validateOptions = {
- "Result" : [],
- "Items" : [],
- "Validate": function(callback){
- for(var i = 0; i < this.Items.length; i++){
- if(validateCallback(this.Items[i]) == false){
- return false;
- }
- }
- callback();
- },
- "Username" :{
- "Required": { "value" : true, "errMsg" : i18n.getMessage("username_is_required")},
- "CheckExist": { "value" : function(value){
- var result = true;
- $.ajax({
- url: "/userExists",
- data: {"target": "username", "value" : value},
- dataType: "json",
- type: "post",
- async: false,
- success: function(data){
- result = data;
- }
- });
- return result;
- }, "errMsg" : i18n.getMessage("username_has_been_taken")},
- "MaxLength": {"value" : 20, "errMsg" : i18n.getMessage("username_is_too_long")},
- "IllegalChar": {"value": [",","~","#", "$", "%"] , "errMsg": i18n.getMessage("username_contains_illegal_chars")}
- },
- "Email" :{
- "Required": { "value" : true, "errMsg" : i18n.getMessage("email_is_required")},
- "RegExp": {"value": /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
- "errMsg": i18n.getMessage("email_contains_illegal_chars")},
- "CheckExist": { "value" : function(value){
- var result = true;
- $.ajax({
- url: "/userExists",
- data: {"target": "email", "value": value},
- dataType: "json",
- type: "post",
- async: false,
- success: function(data){
- result = data;
- }
- });
- return result;
- }, "errMsg" : i18n.getMessage("email_has_been_taken")}
- },
- "EmailF" :{
- "Required": { "value" : true, "errMsg" : i18n.getMessage("email_is_required")},
- "RegExp": {"value": /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
- "errMsg": i18n.getMessage("email_content_illegal")},
- "CheckIfNotExist": { "value" : function(value){
- var result = true;
- $.ajax({
- url: "/userExists",
- data: {"target": "email", "value": value},
- dataType: "json",
- type: "post",
- async: false,
- success: function(data){
- result = data;
- }
- });
- return result;
- }, "errMsg" : i18n.getMessage("email_does_not_exist")}
- },
- "Realname" :{
- "Required": { "value" : true, "errMsg" : i18n.getMessage("realname_is_required")},
- "MaxLength": {"value" : 20, "errMsg" : i18n.getMessage("realname_is_too_long")},
- "IllegalChar": {"value": [",","~","#", "$", "%"] , "errMsg": i18n.getMessage("realname_contains_illegal_chars")}
- },
- "OldPassword" :{
- "Required": { "value" : true, "errMsg" : i18n.getMessage("password_is_required")}
- },
- "Password" :{
- "Required": { "value" : true, "errMsg" : i18n.getMessage("password_is_required")},
- "RegExp": {"value" : /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{7,20}$/, "errMsg" : i18n.getMessage("password_is_invalid")},
- "MaxLength": {"value" : 20, "errMsg" : i18n.getMessage("password_is_too_long")}
- },
- "ConfirmedPassword" :{
- "CompareWith": {"value" : "#Password", "errMsg" : i18n.getMessage("password_does_not_match")},
- "RegExp": {"value" : /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{7,20}$/, "errMsg" : i18n.getMessage("password_is_invalid")}
- },
- "Comment" :{
- "MaxLength": {"value" : 20, "errMsg" : i18n.getMessage("comment_is_too_long")},
- "IllegalChar": {"value": [",","~","#", "$", "%"] , "errMsg": i18n.getMessage("comment_contains_illegal_chars")}
- },
- "projectName" :{
- "Required": { "value" : true, "errMsg" : i18n.getMessage("project_name_is_required")},
- "MinLength": {"value" : 4, "errMsg" : i18n.getMessage("project_name_is_too_short")},
- "MaxLength": {"value" : 30, "errMsg" : i18n.getMessage("project_name_is_too_long")},
- "IllegalChar": {"value": ["~","$","-", "\\", "[", "]", "{", "}", "(", ")", "&", "^", "%", "*", "<", ">", "\"", "'","/","?","@"] , "errMsg": i18n.getMessage("project_name_contains_illegal_chars")}
- }
-};
-function validateCallback(target){
-
- if (typeof target != "string"){
- target = this;
- }
-
- var isValid = true;
- var inputValue = $.trim($(target).val());
- var currentId = $(target).attr("id");
- var validateItem = validateOptions[currentId];
-
- var errMsg = "";
-
- for(var checkTitle in validateItem){
-
- var checkValue = validateItem[checkTitle].value;
-
- if(checkTitle == "Required" && checkValue && inputValue.length == 0){
- isValid = false;
- errMsg = validateItem[checkTitle].errMsg;
- break;
- }else if(checkTitle == "CheckOldPasswordIsCorrect" && checkValue(inputValue) == false){
- isValid = false;
- errMsg = validateItem[checkTitle].errMsg;
- break;
- }else if(checkTitle == "CheckExist" && checkValue(inputValue) == true){
- isValid = false;
- errMsg = validateItem[checkTitle].errMsg;
- break;
- }else if(checkTitle == "CheckIfNotExist" && checkValue(inputValue) == false){
- isValid = false;
- errMsg = validateItem[checkTitle].errMsg;
- break;
- }else if(checkTitle == "RegExp" && checkValue.test(inputValue) == false){
- isValid = false;
- errMsg = validateItem[checkTitle].errMsg;
- break;
- }else if(checkTitle == "MinLength" && inputValue.length < checkValue){
- isValid = false;
- errMsg = validateItem[checkTitle].errMsg;
- break;
- }else if(checkTitle == "MaxLength" && inputValue.length > checkValue){
- isValid = false;
- errMsg = validateItem[checkTitle].errMsg;
- break;
- }else if(checkTitle == "CompareWith" && $.trim($(checkValue).val()).length > 0 && inputValue != $.trim($(checkValue).val())){
- isValid = false;
- errMsg = validateItem[checkTitle].errMsg;
- break;
- }else if(checkTitle == "IllegalChar"){
- for(var i = 0; i < checkValue.length; i++){
- if(inputValue.indexOf(checkValue[i]) > -1){
- isValid = false;
- errMsg = validateItem[checkTitle].errMsg;
- }
- }
- break;
- }
- }
-
- if(isValid == false){
- $(target).parent().removeClass("has-success").addClass("has-error");
- $(target).siblings("span").removeClass("glyphicon-ok").addClass("glyphicon-warning-sign");
- $("#divErrMsg").css({"display": "block"});
- $("#divErrMsg").text(errMsg);
- }else {
- $(target).parent().removeClass("has-error").addClass("has-success");
- $(target).siblings("span").removeClass("glyphicon-warning-sign").addClass("glyphicon-ok");
- $("#divErrMsg").css({"display": "none"});
- }
- validateOptions.Result.push(isValid);
- return isValid;
-}
\ No newline at end of file
diff --git a/static/vendors/angularjs/angular-cookies.min.js b/static/vendors/angularjs/angular-cookies.min.js
new file mode 100644
index 000000000..38eec5bf3
--- /dev/null
+++ b/static/vendors/angularjs/angular-cookies.min.js
@@ -0,0 +1,9 @@
+/*
+ AngularJS v1.5.3
+ (c) 2010-2016 Google, Inc. http://angularjs.org
+ License: MIT
+*/
+(function(p,c,n){'use strict';function l(b,a,g){var d=g.baseHref(),k=b[0];return function(b,e,f){var g,h;f=f||{};h=f.expires;g=c.isDefined(f.path)?f.path:d;c.isUndefined(e)&&(h="Thu, 01 Jan 1970 00:00:00 GMT",e="");c.isString(h)&&(h=new Date(h));e=encodeURIComponent(b)+"="+encodeURIComponent(e);e=e+(g?";path="+g:"")+(f.domain?";domain="+f.domain:"");e+=h?";expires="+h.toUTCString():"";e+=f.secure?";secure":"";f=e.length+1;4096 4096 bytes)!");k.cookie=e}}c.module("ngCookies",["ng"]).provider("$cookies",[function(){var b=this.defaults={};this.$get=["$$cookieReader","$$cookieWriter",function(a,g){return{get:function(d){return a()[d]},getObject:function(d){return(d=this.get(d))?c.fromJson(d):d},getAll:function(){return a()},put:function(d,a,m){g(d,a,m?c.extend({},b,m):b)},putObject:function(d,b,a){this.put(d,c.toJson(b),a)},remove:function(a,k){g(a,n,k?c.extend({},b,k):b)}}}]}]);c.module("ngCookies").factory("$cookieStore",
+["$cookies",function(b){return{get:function(a){return b.getObject(a)},put:function(a,c){b.putObject(a,c)},remove:function(a){b.remove(a)}}}]);l.$inject=["$document","$log","$browser"];c.module("ngCookies").provider("$$cookieWriter",function(){this.$get=l})})(window,window.angular);
+//# sourceMappingURL=angular-cookies.min.js.map
diff --git a/static/vendors/angularjs/angular-messages.min.js b/static/vendors/angularjs/angular-messages.min.js
new file mode 100644
index 000000000..e3094439e
--- /dev/null
+++ b/static/vendors/angularjs/angular-messages.min.js
@@ -0,0 +1,12 @@
+/*
+ AngularJS v1.5.3
+ (c) 2010-2016 Google, Inc. http://angularjs.org
+ License: MIT
+*/
+(function(A,d,B){'use strict';function p(){return["$animate",function(w){return{restrict:"AE",transclude:"element",priority:1,terminal:!0,require:"^^ngMessages",link:function(n,l,a,c,m){var k=l[0],f,q=a.ngMessage||a.when;a=a.ngMessageExp||a.whenExp;var d=function(a){f=a?x(a)?a:a.split(/[\s,]+/):null;c.reRender()};a?(d(n.$eval(a)),n.$watchCollection(a,d)):d(q);var e,r;c.register(k,r={test:function(a){var g=f;a=g?x(g)?0<=g.indexOf(a):g.hasOwnProperty(a):void 0;return a},attach:function(){e||m(n,function(a){w.enter(a,
+null,l);e=a;var g=e.$$attachId=c.getAttachId();e.on("$destroy",function(){e&&e.$$attachId===g&&(c.deregister(k),r.detach())})})},detach:function(){if(e){var a=e;e=null;w.leave(a)}}})}}}]}var x=d.isArray,t=d.forEach,y=d.isString,z=d.element;d.module("ngMessages",[]).directive("ngMessages",["$animate",function(d){function n(a,c){return y(c)&&0===c.length||l(a.$eval(c))}function l(a){return y(a)?a.length:!!a}return{require:"ngMessages",restrict:"AE",controller:["$element","$scope","$attrs",function(a,
+c,m){function k(a,c){for(var b=c,f=[];b&&b!==a;){var h=b.$$ngMessageNode;if(h&&h.length)return e[h];b.childNodes.length&&-1==f.indexOf(b)?(f.push(b),b=b.childNodes[b.childNodes.length-1]):b.previousSibling?b=b.previousSibling:(b=b.parentNode,f.push(b))}}var f=this,q=0,p=0;this.getAttachId=function(){return p++};var e=this.messages={},r,s;this.render=function(g){g=g||{};r=!1;s=g;for(var e=n(c,m.ngMessagesMultiple)||n(c,m.multiple),b=[],q={},h=f.head,k=!1,p=0;null!=h;){p++;var u=h.message,v=!1;k||t(g,
+function(a,b){!v&&l(a)&&u.test(b)&&!q[b]&&(v=q[b]=!0,u.attach())});v?k=!e:b.push(u);h=h.next}t(b,function(a){a.detach()});b.length!==p?d.setClass(a,"ng-active","ng-inactive"):d.setClass(a,"ng-inactive","ng-active")};c.$watchCollection(m.ngMessages||m["for"],f.render);a.on("$destroy",function(){t(e,function(a){a.message.detach()})});this.reRender=function(){r||(r=!0,c.$evalAsync(function(){r&&s&&f.render(s)}))};this.register=function(g,c){var b=q.toString();e[b]={message:c};var d=a[0],h=e[b];f.head?
+(d=k(d,g))?(h.next=d.next,d.next=h):(h.next=f.head,f.head=h):f.head=h;g.$$ngMessageNode=b;q++;f.reRender()};this.deregister=function(c){var d=c.$$ngMessageNode;delete c.$$ngMessageNode;var b=e[d];(c=k(a[0],c))?c.next=b.next:f.head=b.next;delete e[d];f.reRender()}}]}}]).directive("ngMessagesInclude",["$templateRequest","$document","$compile",function(d,n,l){return{restrict:"AE",require:"^^ngMessages",link:function(a,c,m){var k=m.ngMessagesInclude||m.src;d(k).then(function(d){l(d)(a,function(a){c.after(a);
+a=l.$$createComment?l.$$createComment("ngMessagesInclude",k):n[0].createComment(" ngMessagesInclude: "+k+" ");a=z(a);c.after(a);c.remove()})})}}}]).directive("ngMessage",p()).directive("ngMessageExp",p())})(window,window.angular);
+//# sourceMappingURL=angular-messages.min.js.map
diff --git a/static/vendors/angularjs/angular.min.js b/static/vendors/angularjs/angular.min.js
new file mode 100644
index 000000000..ecdf96736
--- /dev/null
+++ b/static/vendors/angularjs/angular.min.js
@@ -0,0 +1,311 @@
+/*
+ AngularJS v1.5.3
+ (c) 2010-2016 Google, Inc. http://angularjs.org
+ License: MIT
+*/
+(function(T,P,u){'use strict';function O(a){return function(){var b=arguments[0],d;d="["+(a?a+":":"")+b+"] http://errors.angularjs.org/1.5.3/"+(a?a+"/":"")+b;for(b=1;b").append(a).html();try{return a[0].nodeType===Pa?N(d):d.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+N(b)})}catch(c){return N(d)}}function wc(a){try{return decodeURIComponent(a)}catch(b){}}
+function xc(a){var b={};q((a||"").split("&"),function(a){var c,e,f;a&&(e=a=a.replace(/\+/g,"%20"),c=a.indexOf("="),-1!==c&&(e=a.substring(0,c),f=a.substring(c+1)),e=wc(e),A(e)&&(f=A(f)?wc(f):!0,va.call(b,e)?M(b[e])?b[e].push(f):b[e]=[b[e],f]:b[e]=f))});return b}function Sb(a){var b=[];q(a,function(a,c){M(a)?q(a,function(a){b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))}):b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))});return b.length?b.join("&"):""}function rb(a){return ja(a,!0).replace(/%26/gi,"&").replace(/%3D/gi,
+"=").replace(/%2B/gi,"+")}function ja(a,b){return encodeURIComponent(a).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,b?"%20":"+")}function ce(a,b){var d,c,e=Qa.length;for(c=0;c/,">"));}b=b||[];b.unshift(["$provide",function(b){b.value("$rootElement",a)}]);d.debugInfoEnabled&&b.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);b.unshift("ng");c=eb(b,d.strictDi);c.invoke(["$rootScope",
+"$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;T&&e.test(T.name)&&(d.debugInfoEnabled=!0,T.name=T.name.replace(e,""));if(T&&!f.test(T.name))return c();T.name=T.name.replace(f,"");ea.resumeBootstrap=function(a){q(a,function(a){b.push(a)});return c()};D(ea.resumeDeferredBootstrap)&&ea.resumeDeferredBootstrap()}function ee(){T.name="NG_ENABLE_DEBUG_INFO!"+T.name;T.location.reload()}
+function fe(a){a=ea.element(a).injector();if(!a)throw Ba("test");return a.get("$$testability")}function zc(a,b){b=b||"_";return a.replace(ge,function(a,c){return(c?b:"")+a.toLowerCase()})}function he(){var a;if(!Ac){var b=sb();($=z(b)?T.jQuery:b?T[b]:u)&&$.fn.on?(H=$,S($.fn,{scope:Ra.scope,isolateScope:Ra.isolateScope,controller:Ra.controller,injector:Ra.injector,inheritedData:Ra.inheritedData}),a=$.cleanData,$.cleanData=function(b){for(var c,e=0,f;null!=(f=b[e]);e++)(c=$._data(f,"events"))&&c.$destroy&&
+$(f).triggerHandler("$destroy");a(b)}):H=U;ea.element=H;Ac=!0}}function tb(a,b,d){if(!a)throw Ba("areq",b||"?",d||"required");return a}function Sa(a,b,d){d&&M(a)&&(a=a[a.length-1]);tb(D(a),b,"not a function, got "+(a&&"object"===typeof a?a.constructor.name||"Object":typeof a));return a}function Ta(a,b){if("hasOwnProperty"===a)throw Ba("badname",b);}function Bc(a,b,d){if(!b)return a;b=b.split(".");for(var c,e=a,f=b.length,g=0;g$2>")+c[2];for(c=c[0];c--;)d=d.lastChild;f=cb(f,d.childNodes);d=e.firstChild;d.textContent=""}else f.push(b.createTextNode(a));e.textContent="";e.innerHTML="";q(f,function(a){e.appendChild(a)});return e}function Mc(a,
+b){var d=a.parentNode;d&&d.replaceChild(b,a);b.appendChild(a)}function U(a){if(a instanceof U)return a;var b;y(a)&&(a=W(a),b=!0);if(!(this instanceof U)){if(b&&"<"!=a.charAt(0))throw Vb("nosel");return new U(a)}if(b){b=P;var d;a=(d=Lf.exec(a))?[b.createElement(d[1])]:(d=Lc(a,b))?d.childNodes:[]}Nc(this,a)}function Wb(a){return a.cloneNode(!0)}function xb(a,b){b||gb(a);if(a.querySelectorAll)for(var d=a.querySelectorAll("*"),c=0,e=d.length;c=Da?!1:"function"===typeof a&&/^(?:class\s|constructor\()/.test(Function.prototype.toString.call(a));return d?(c.unshift(null),new (Function.prototype.bind.apply(a,c))):a.apply(b,c)},instantiate:function(a,b,c){var d=M(a)?a[a.length-1]:a;a=e(a,b,c);a.unshift(null);return new (Function.prototype.bind.apply(d,a))},get:d,annotate:eb.$$annotate,has:function(b){return n.hasOwnProperty(b+
+"Provider")||a.hasOwnProperty(b)}}}b=!0===b;var k={},l=[],m=new Ua([],!0),n={$provide:{provider:d(c),factory:d(f),service:d(function(a,b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:d(function(a,b){return f(a,da(b),!1)}),constant:d(function(a,b){Ta(a,"constant");n[a]=b;F[a]=b}),decorator:function(a,b){var c=p.get(a+"Provider"),d=c.$get;c.$get=function(){var a=x.invoke(d,c);return x.invoke(b,null,{$delegate:a})}}}},p=n.$injector=h(n,function(a,b){ea.isString(b)&&l.push(b);
+throw Ia("unpr",l.join(" <- "));}),F={},L=h(F,function(a,b){var c=p.get(a+"Provider",b);return x.invoke(c.$get,c,u,a)}),x=L;n.$injectorProvider={$get:da(L)};var r=g(a),x=L.get("$injector");x.strictDi=b;q(r,function(a){a&&x.invoke(a)});return x}function Ve(){var a=!0;this.disableAutoScrolling=function(){a=!1};this.$get=["$window","$location","$rootScope",function(b,d,c){function e(a){var b=null;Array.prototype.some.call(a,function(a){if("a"===oa(a))return b=a,!0});return b}function f(a){if(a){a.scrollIntoView();
+var c;c=g.yOffset;D(c)?c=c():Pb(c)?(c=c[0],c="fixed"!==b.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):R(c)||(c=0);c&&(a=a.getBoundingClientRect().top,b.scrollBy(0,a-c))}else b.scrollTo(0,0)}function g(a){a=y(a)?a:d.hash();var b;a?(b=h.getElementById(a))?f(b):(b=e(h.getElementsByName(a)))?f(b):"top"===a&&f(null):f(null)}var h=b.document;a&&c.$watch(function(){return d.hash()},function(a,b){a===b&&""===a||Nf(function(){c.$evalAsync(g)})});return g}]}function ib(a,b){if(!a&&!b)return"";
+if(!a)return b;if(!b)return a;M(a)&&(a=a.join(" "));M(b)&&(b=b.join(" "));return a+" "+b}function Wf(a){y(a)&&(a=a.split(" "));var b=V();q(a,function(a){a.length&&(b[a]=!0)});return b}function Ja(a){return J(a)?a:{}}function Xf(a,b,d,c){function e(a){try{a.apply(null,Aa.call(arguments,1))}finally{if(L--,0===L)for(;x.length;)try{x.pop()()}catch(b){d.error(b)}}}function f(){t=null;g();h()}function g(){r=G();r=z(r)?null:r;na(r,I)&&(r=I);I=r}function h(){if(v!==k.url()||w!==r)v=k.url(),w=r,q(C,function(a){a(k.url(),
+r)})}var k=this,l=a.location,m=a.history,n=a.setTimeout,p=a.clearTimeout,F={};k.isMock=!1;var L=0,x=[];k.$$completeOutstandingRequest=e;k.$$incOutstandingRequestCount=function(){L++};k.notifyWhenNoOutstandingRequests=function(a){0===L?a():x.push(a)};var r,w,v=l.href,Q=b.find("base"),t=null,G=c.history?function(){try{return m.state}catch(a){}}:E;g();w=r;k.url=function(b,d,e){z(e)&&(e=null);l!==a.location&&(l=a.location);m!==a.history&&(m=a.history);if(b){var f=w===e;if(v===b&&(!c.history||f))return k;
+var h=v&&Ka(v)===Ka(b);v=b;w=e;if(!c.history||h&&f){if(!h||t)t=b;d?l.replace(b):h?(d=l,e=b.indexOf("#"),e=-1===e?"":b.substr(e),d.hash=e):l.href=b;l.href!==b&&(t=b)}else m[d?"replaceState":"pushState"](e,"",b),g(),w=r;return k}return t||l.href.replace(/%27/g,"'")};k.state=function(){return r};var C=[],K=!1,I=null;k.onUrlChange=function(b){if(!K){if(c.history)H(a).on("popstate",f);H(a).on("hashchange",f);K=!0}C.push(b);return b};k.$$applicationDestroyed=function(){H(a).off("hashchange popstate",f)};
+k.$$checkUrlChange=h;k.baseHref=function(){var a=Q.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};k.defer=function(a,b){var c;L++;c=n(function(){delete F[c];e(a)},b||0);F[c]=!0;return c};k.defer.cancel=function(a){return F[a]?(delete F[a],p(a),e(E),!0):!1}}function bf(){this.$get=["$window","$log","$sniffer","$document",function(a,b,d,c){return new Xf(a,c,b,d)}]}function cf(){this.$get=function(){function a(a,c){function e(a){a!=n&&(p?p==a&&(p=a.n):p=a,f(a.n,a.p),f(a,n),n=a,n.n=
+null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(a in b)throw O("$cacheFactory")("iid",a);var g=0,h=S({},c,{id:a}),k=V(),l=c&&c.capacity||Number.MAX_VALUE,m=V(),n=null,p=null;return b[a]={put:function(a,b){if(!z(b)){if(ll&&this.remove(p.key);return b}},get:function(a){if(l";b=la.firstChild.attributes;var d=b[0];b.removeNamedItem(d.name);
+d.value=c;a.attributes.setNamedItem(d)}function B(a,b){try{a.addClass(b)}catch(c){}}function ba(a,b,c,d,e){a instanceof H||(a=H(a));for(var f=/\S+/,g=0,h=a.length;g").append(a).html())):c?Ra.clone.call(a):a;if(g)for(var h in g)d.data("$"+h+"Controller",g[h].instance);ba.$$addScopeInfo(d,b);c&&c(d,b);l&&l(b,d,d,f);return d}}function xa(a,b,c,d,e,f){function g(a,c,d,e){var f,k,l,m,n,p,G;if(r)for(G=Array(c.length),m=0;mB.priority)break;if(y=B.scope)B.templateUrl||(J(y)?(X("new/isolated scope",C||G,B,t),C=B):X("new/isolated scope",C,B,t)),G=G||B;L=B.name;if(!Ea&&(B.replace&&(B.templateUrl||B.template)||B.transclude&&!B.$$tlb)){for(y=ra+1;Ea=a[y++];)if(Ea.transclude&&!Ea.$$tlb||Ea.replace&&(Ea.templateUrl||Ea.template)){E=!0;break}Ea=!0}!B.templateUrl&&
+B.controller&&(y=B.controller,v=v||V(),X("'"+L+"' controller",v[L],B,t),v[L]=B);if(y=B.transclude)if(K=!0,B.$$tlb||(X("transclusion",I,B,t),I=B),"element"==y)Ca=!0,p=B.priority,Q=t,t=d.$$element=H(ba.$$createComment(L,d[L])),b=t[0],da(f,Aa.call(Q,0),b),Q[0].$$parentNode=Q[0].parentNode,s=Zb(E,Q,e,p,g&&g.name,{nonTlbTranscludeDirective:I});else{var P=V();Q=H(Wb(b)).contents();if(J(y)){Q=[];var Z=V(),Y=V();q(y,function(a,b){var c="?"===a.charAt(0);a=c?a.substring(1):a;Z[a]=b;P[b]=null;Y[b]=c});q(t.contents(),
+function(a){var b=Z[ya(oa(a))];b?(Y[b]=!0,P[b]=P[b]||[],P[b].push(a)):Q.push(a)});q(Y,function(a,b){if(!a)throw ga("reqslot",b);});for(var $ in P)P[$]&&(P[$]=Zb(E,P[$],e))}t.empty();s=Zb(E,Q,e,u,u,{needsNewScope:B.$$isolateScope||B.$$newScope});s.$$slots=P}if(B.template)if(x=!0,X("template",w,B,t),w=B,y=D(B.template)?B.template(t,d):B.template,y=ua(y),B.replace){g=B;Q=Ub.test(y)?Xc(ca(B.templateNamespace,W(y))):[];b=Q[0];if(1!=Q.length||1!==b.nodeType)throw ga("tplrt",L,"");da(f,t,b);N={$attr:{}};
+y=A(b,[],N);var ea=a.splice(ra+1,a.length-(ra+1));(C||G)&&Yc(y,C,G);a=a.concat(y).concat(ea);U(d,N);N=a.length}else t.html(y);if(B.templateUrl)x=!0,X("template",w,B,t),w=B,B.replace&&(g=B),n=aa(a.splice(ra,a.length-ra),t,d,f,K&&s,h,k,{controllerDirectives:v,newScopeDirective:G!==B&&G,newIsolateScopeDirective:C,templateDirective:w,nonTlbTranscludeDirective:I}),N=a.length;else if(B.compile)try{xa=B.compile(t,d,s),D(xa)?m(null,xa,R,Fa):xa&&m(xa.pre,xa.post,R,Fa)}catch(fa){c(fa,wa(t))}B.terminal&&(n.terminal=
+!0,p=Math.max(p,B.priority))}n.scope=G&&!0===G.scope;n.transcludeOnThisElement=K;n.templateOnThisElement=x;n.transclude=s;l.hasElementTranscludeDirective=Ca;return n}function jb(a,b,c,d){var e;if(y(b)){var f=b.match(k);b=b.substring(f[0].length);var g=f[1]||f[3],f="?"===f[2];"^^"===g?c=c.parent():e=(e=d&&d[b])&&e.instance;if(!e){var h="$"+b+"Controller";e=g?c.inheritedData(h):c.data(h)}if(!e&&!f)throw ga("ctreq",b,a);}else if(M(b))for(e=[],g=0,f=b.length;gn.priority)&&-1!=n.restrict.indexOf(g)){l&&(n=Qb(n,{$$start:l,$$end:m}));if(!n.$$bindings){var v=n,C=n,w=n.name,B={isolateScope:null,bindToController:null};J(C.scope)&&(!0===C.bindToController?(B.bindToController=d(C.scope,w,!0),B.isolateScope={}):B.isolateScope=d(C.scope,w,!1));J(C.bindToController)&&(B.bindToController=d(C.bindToController,w,!0));if(J(B.bindToController)){var I=C.controller,K=C.controllerAs;if(!I)throw ga("noctrl",
+w);if(!Uc(I,K))throw ga("noident",w);}var x=v.$$bindings=B;J(x.isolateScope)&&(n.$$isolateBindings=x.isolateScope)}b.push(n);k=n}}catch(t){c(t)}}return k}function R(b){if(e.hasOwnProperty(b))for(var c=a.get(b+"Directive"),d=0,f=c.length;d"+b+""+a+">";return c.childNodes[0].childNodes;default:return b}}function ea(a,b){if("srcdoc"==b)return G.HTML;var c=oa(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return G.RESOURCE_URL}
+function fa(a,c,d,e,f){var g=ea(a,e);f=h[e]||f;var k=b(d,!0,g,f);if(k){if("multiple"===e&&"select"===oa(a))throw ga("selmulti",wa(a));c.push({priority:100,compile:function(){return{pre:function(a,c,h){c=h.$$observers||(h.$$observers=V());if(l.test(e))throw ga("nodomevents");var m=h[e];m!==d&&(k=m&&b(m,!0,g,f),d=m);k&&(h[e]=k(a),(c[e]||(c[e]=[])).$$inter=!0,(h.$$observers&&h.$$observers[e].$$scope||a).$watch(k,function(a,b){"class"===e&&a!=b?h.$updateClass(a,b):h.$set(e,a)}))}}}})}}function da(a,b,
+c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g=b)return a;for(;b--;)8===a[b].nodeType&&
+Yf.call(a,b,1);return a}function Uc(a,b){if(b&&y(b))return b;if(y(a)){var d=ad.exec(a);if(d)return d[3]}}function df(){var a={},b=!1;this.has=function(b){return a.hasOwnProperty(b)};this.register=function(b,c){Ta(b,"controller");J(b)?S(a,b):a[b]=c};this.allowGlobals=function(){b=!0};this.$get=["$injector","$window",function(d,c){function e(a,b,c,d){if(!a||!J(a.$scope))throw O("$controller")("noscp",d,b);a.$scope[b]=c}return function(f,g,h,k){var l,m,n;h=!0===h;k&&y(k)&&(n=k);if(y(f)){k=f.match(ad);
+if(!k)throw Zf("ctrlfmt",f);m=k[1];n=n||k[3];f=a.hasOwnProperty(m)?a[m]:Bc(g.$scope,m,!0)||(b?Bc(c,m,!0):u);Sa(f,m,!0)}if(h)return h=(M(f)?f[f.length-1]:f).prototype,l=Object.create(h||null),n&&e(g,n,l,m||f.name),S(function(){var a=d.invoke(f,l,g,m);a!==l&&(J(a)||D(a))&&(l=a,n&&e(g,n,l,m||f.name));return l},{instance:l,identifier:n});l=d.instantiate(f,g,m);n&&e(g,n,l,m||f.name);return l}}]}function ef(){this.$get=["$window",function(a){return H(a.document)}]}function ff(){this.$get=["$log",function(a){return function(b,
+d){a.error.apply(a,arguments)}}]}function $b(a){return J(a)?fa(a)?a.toISOString():db(a):a}function lf(){this.$get=function(){return function(a){if(!a)return"";var b=[];pc(a,function(a,c){null===a||z(a)||(M(a)?q(a,function(a){b.push(ja(c)+"="+ja($b(a)))}):b.push(ja(c)+"="+ja($b(a))))});return b.join("&")}}}function mf(){this.$get=function(){return function(a){function b(a,e,f){null===a||z(a)||(M(a)?q(a,function(a,c){b(a,e+"["+(J(a)?c:"")+"]")}):J(a)&&!fa(a)?pc(a,function(a,c){b(a,e+(f?"":"[")+c+(f?
+"":"]"))}):d.push(ja(e)+"="+ja($b(a))))}if(!a)return"";var d=[];b(a,"",!0);return d.join("&")}}}function ac(a,b){if(y(a)){var d=a.replace($f,"").trim();if(d){var c=b("Content-Type");(c=c&&0===c.indexOf(bd))||(c=(c=d.match(ag))&&bg[c[0]].test(d));c&&(a=uc(d))}}return a}function cd(a){var b=V(),d;y(a)?q(a.split("\n"),function(a){d=a.indexOf(":");var e=N(W(a.substr(0,d)));a=W(a.substr(d+1));e&&(b[e]=b[e]?b[e]+", "+a:a)}):J(a)&&q(a,function(a,d){var f=N(d),g=W(a);f&&(b[f]=b[f]?b[f]+", "+g:g)});return b}
+function dd(a){var b;return function(d){b||(b=cd(a));return d?(d=b[N(d)],void 0===d&&(d=null),d):b}}function ed(a,b,d,c){if(D(c))return c(a,b,d);q(c,function(c){a=c(a,b,d)});return a}function kf(){var a=this.defaults={transformResponse:[ac],transformRequest:[function(a){return J(a)&&"[object File]"!==ka.call(a)&&"[object Blob]"!==ka.call(a)&&"[object FormData]"!==ka.call(a)?db(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ia(bc),put:ia(bc),patch:ia(bc)},xsrfCookieName:"XSRF-TOKEN",
+xsrfHeaderName:"X-XSRF-TOKEN",paramSerializer:"$httpParamSerializer"},b=!1;this.useApplyAsync=function(a){return A(a)?(b=!!a,this):b};var d=!0;this.useLegacyPromiseExtensions=function(a){return A(a)?(d=!!a,this):d};var c=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector",function(e,f,g,h,k,l){function m(b){function c(a){var b=S({},a);b.data=ed(a.data,a.headers,a.status,f.transformResponse);a=a.status;return 200<=a&&300>a?b:k.reject(b)}function e(a,
+b){var c,d={};q(a,function(a,e){D(a)?(c=a(b),null!=c&&(d[e]=c)):d[e]=a});return d}if(!J(b))throw O("$http")("badreq",b);if(!y(b.url))throw O("$http")("badreq",b.url);var f=S({method:"get",transformRequest:a.transformRequest,transformResponse:a.transformResponse,paramSerializer:a.paramSerializer},b);f.headers=function(b){var c=a.headers,d=S({},b.headers),f,g,h,c=S({},c.common,c[N(b.method)]);a:for(f in c){g=N(f);for(h in d)if(N(h)===g)continue a;d[f]=c[f]}return e(d,ia(b))}(b);f.method=vb(f.method);
+f.paramSerializer=y(f.paramSerializer)?l.get(f.paramSerializer):f.paramSerializer;var g=[function(b){var d=b.headers,e=ed(b.data,dd(d),u,b.transformRequest);z(e)&&q(d,function(a,b){"content-type"===N(b)&&delete d[b]});z(b.withCredentials)&&!z(a.withCredentials)&&(b.withCredentials=a.withCredentials);return n(b,e).then(c,c)},u],h=k.when(f);for(q(L,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){b=
+g.shift();var m=g.shift(),h=h.then(b,m)}d?(h.success=function(a){Sa(a,"fn");h.then(function(b){a(b.data,b.status,b.headers,f)});return h},h.error=function(a){Sa(a,"fn");h.then(null,function(b){a(b.data,b.status,b.headers,f)});return h}):(h.success=fd("success"),h.error=fd("error"));return h}function n(c,d){function g(a,c,d,e){function f(){l(c,a,d,e)}K&&(200<=a&&300>a?K.put(L,[a,c,cd(d),e]):K.remove(L));b?h.$applyAsync(f):(f(),h.$$phase||h.$apply())}function l(a,b,d,e){b=-1<=b?b:0;(200<=b&&300>b?G.resolve:
+G.reject)({data:a,status:b,headers:dd(d),config:c,statusText:e})}function n(a){l(a.data,a.status,ia(a.headers()),a.statusText)}function t(){var a=m.pendingRequests.indexOf(c);-1!==a&&m.pendingRequests.splice(a,1)}var G=k.defer(),C=G.promise,K,I,qa=c.headers,L=p(c.url,c.paramSerializer(c.params));m.pendingRequests.push(c);C.then(t,t);!c.cache&&!a.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(K=J(c.cache)?c.cache:J(a.cache)?a.cache:F);K&&(I=K.get(L),A(I)?I&&D(I.then)?I.then(n,n):M(I)?
+l(I[1],I[0],ia(I[2]),I[3]):l(I,200,{},"OK"):K.put(L,C));z(I)&&((I=gd(c.url)?f()[c.xsrfCookieName||a.xsrfCookieName]:u)&&(qa[c.xsrfHeaderName||a.xsrfHeaderName]=I),e(c.method,L,d,g,qa,c.timeout,c.withCredentials,c.responseType));return C}function p(a,b){0=l&&(v.resolve(r),x(Q.$$intervalId),delete g[Q.$$intervalId]);w||a.$apply()},k);g[Q.$$intervalId]=v;return Q}var g={};f.cancel=function(a){return a&&a.$$intervalId in g?(g[a.$$intervalId].reject("canceled"),b.clearInterval(a.$$intervalId),
+delete g[a.$$intervalId],!0):!1};return f}]}function cc(a){a=a.split("/");for(var b=a.length;b--;)a[b]=rb(a[b]);return a.join("/")}function hd(a,b){var d=sa(a);b.$$protocol=d.protocol;b.$$host=d.hostname;b.$$port=Y(d.port)||dg[d.protocol]||null}function id(a,b){var d="/"!==a.charAt(0);d&&(a="/"+a);var c=sa(a);b.$$path=decodeURIComponent(d&&"/"===c.pathname.charAt(0)?c.pathname.substring(1):c.pathname);b.$$search=xc(c.search);b.$$hash=decodeURIComponent(c.hash);b.$$path&&"/"!=b.$$path.charAt(0)&&(b.$$path=
+"/"+b.$$path)}function la(a,b){if(0===b.indexOf(a))return b.substr(a.length)}function Ka(a){var b=a.indexOf("#");return-1==b?a:a.substr(0,b)}function kb(a){return a.replace(/(#.+)|#$/,"$1")}function dc(a,b,d){this.$$html5=!0;d=d||"";hd(a,this);this.$$parse=function(a){var d=la(b,a);if(!y(d))throw Fb("ipthprfx",a,b);id(d,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Sb(this.$$search),d=this.$$hash?"#"+rb(this.$$hash):"";this.$$url=cc(this.$$path)+(a?"?"+a:"")+
+d;this.$$absUrl=b+this.$$url.substr(1)};this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;A(f=la(a,c))?(g=f,g=A(f=la(d,f))?b+(la("/",f)||f):a+g):A(f=la(b,c))?g=b+f:b==c+"/"&&(g=b);g&&this.$$parse(g);return!!g}}function ec(a,b,d){hd(a,this);this.$$parse=function(c){var e=la(a,c)||la(b,c),f;z(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",z(e)&&(a=c,this.replace())):(f=la(d,e),z(f)&&(f=e));id(f,this);c=this.$$path;var e=a,g=/^\/[A-Z]:(\/.*)/;0===f.indexOf(e)&&
+(f=f.replace(e,""));g.exec(f)||(c=(f=g.exec(c))?f[1]:c);this.$$path=c;this.$$compose()};this.$$compose=function(){var b=Sb(this.$$search),e=this.$$hash?"#"+rb(this.$$hash):"";this.$$url=cc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+(this.$$url?d+this.$$url:"")};this.$$parseLinkUrl=function(b,d){return Ka(a)==Ka(b)?(this.$$parse(b),!0):!1}}function jd(a,b,d){this.$$html5=!0;ec.apply(this,arguments);this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;a==Ka(c)?
+f=c:(g=la(b,c))?f=a+d+g:b===c+"/"&&(f=b);f&&this.$$parse(f);return!!f};this.$$compose=function(){var b=Sb(this.$$search),e=this.$$hash?"#"+rb(this.$$hash):"";this.$$url=cc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+d+this.$$url}}function Gb(a){return function(){return this[a]}}function kd(a,b){return function(d){if(z(d))return this[a];this[a]=b(d);this.$$compose();return this}}function pf(){var a="",b={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(b){return A(b)?(a=b,this):
+a};this.html5Mode=function(a){return Oa(a)?(b.enabled=a,this):J(a)?(Oa(a.enabled)&&(b.enabled=a.enabled),Oa(a.requireBase)&&(b.requireBase=a.requireBase),Oa(a.rewriteLinks)&&(b.rewriteLinks=a.rewriteLinks),this):b};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",function(d,c,e,f,g){function h(a,b,d){var e=l.url(),f=l.$$state;try{c.url(a,b,d),l.$$state=c.state()}catch(g){throw l.url(e),l.$$state=f,g;}}function k(a,b){d.$broadcast("$locationChangeSuccess",l.absUrl(),a,l.$$state,
+b)}var l,m;m=c.baseHref();var n=c.url(),p;if(b.enabled){if(!m&&b.requireBase)throw Fb("nobase");p=n.substring(0,n.indexOf("/",n.indexOf("//")+2))+(m||"/");m=e.history?dc:jd}else p=Ka(n),m=ec;var F=p.substr(0,Ka(p).lastIndexOf("/")+1);l=new m(p,F,"#"+a);l.$$parseLinkUrl(n,n);l.$$state=c.state();var q=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(b.rewriteLinks&&!a.ctrlKey&&!a.metaKey&&!a.shiftKey&&2!=a.which&&2!=a.button){for(var e=H(a.target);"a"!==oa(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;
+var h=e.prop("href"),k=e.attr("href")||e.attr("xlink:href");J(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=sa(h.animVal).href);q.test(h)||!h||e.attr("target")||a.isDefaultPrevented()||!l.$$parseLinkUrl(h,k)||(a.preventDefault(),l.absUrl()!=c.url()&&(d.$apply(),g.angular["ff-684208-preventDefault"]=!0))}});kb(l.absUrl())!=kb(n)&&c.url(l.absUrl(),!0);var x=!0;c.onUrlChange(function(a,b){z(la(F,a))?g.location.href=a:(d.$evalAsync(function(){var c=l.absUrl(),e=l.$$state,f;a=kb(a);l.$$parse(a);
+l.$$state=b;f=d.$broadcast("$locationChangeStart",a,c,b,e).defaultPrevented;l.absUrl()===a&&(f?(l.$$parse(c),l.$$state=e,h(c,!1,e)):(x=!1,k(c,e)))}),d.$$phase||d.$digest())});d.$watch(function(){var a=kb(c.url()),b=kb(l.absUrl()),f=c.state(),g=l.$$replace,m=a!==b||l.$$html5&&e.history&&f!==l.$$state;if(x||m)x=!1,d.$evalAsync(function(){var b=l.absUrl(),c=d.$broadcast("$locationChangeStart",b,a,l.$$state,f).defaultPrevented;l.absUrl()===b&&(c?(l.$$parse(a),l.$$state=f):(m&&h(b,g,f===l.$$state?null:
+l.$$state),k(a,f)))});l.$$replace=!1});return l}]}function qf(){var a=!0,b=this;this.debugEnabled=function(b){return A(b)?(a=b,this):a};this.$get=["$window",function(d){function c(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=d.console||{},e=b[a]||b.log||E;a=!1;try{a=!!e.apply}catch(k){}return a?function(){var a=[];q(arguments,function(b){a.push(c(b))});
+return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){a&&c.apply(b,arguments)}}()}}]}function Wa(a,b){if("__defineGetter__"===a||"__defineSetter__"===a||"__lookupGetter__"===a||"__lookupSetter__"===a||"__proto__"===a)throw ca("isecfld",b);return a}function eg(a){return a+""}function ta(a,b){if(a){if(a.constructor===a)throw ca("isecfn",b);if(a.window===a)throw ca("isecwindow",b);if(a.children&&
+(a.nodeName||a.prop&&a.attr&&a.find))throw ca("isecdom",b);if(a===Object)throw ca("isecobj",b);}return a}function ld(a,b){if(a){if(a.constructor===a)throw ca("isecfn",b);if(a===fg||a===gg||a===hg)throw ca("isecff",b);}}function Hb(a,b){if(a&&(a===(0).constructor||a===(!1).constructor||a==="".constructor||a==={}.constructor||a===[].constructor||a===Function.constructor))throw ca("isecaf",b);}function ig(a,b){return"undefined"!==typeof a?a:b}function md(a,b){return"undefined"===typeof a?b:"undefined"===
+typeof b?a:a+b}function aa(a,b){var d,c;switch(a.type){case s.Program:d=!0;q(a.body,function(a){aa(a.expression,b);d=d&&a.expression.constant});a.constant=d;break;case s.Literal:a.constant=!0;a.toWatch=[];break;case s.UnaryExpression:aa(a.argument,b);a.constant=a.argument.constant;a.toWatch=a.argument.toWatch;break;case s.BinaryExpression:aa(a.left,b);aa(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.left.toWatch.concat(a.right.toWatch);break;case s.LogicalExpression:aa(a.left,
+b);aa(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.constant?[]:[a];break;case s.ConditionalExpression:aa(a.test,b);aa(a.alternate,b);aa(a.consequent,b);a.constant=a.test.constant&&a.alternate.constant&&a.consequent.constant;a.toWatch=a.constant?[]:[a];break;case s.Identifier:a.constant=!1;a.toWatch=[a];break;case s.MemberExpression:aa(a.object,b);a.computed&&aa(a.property,b);a.constant=a.object.constant&&(!a.computed||a.property.constant);a.toWatch=[a];break;case s.CallExpression:d=
+a.filter?!b(a.callee.name).$stateful:!1;c=[];q(a.arguments,function(a){aa(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=a.filter&&!b(a.callee.name).$stateful?c:[a];break;case s.AssignmentExpression:aa(a.left,b);aa(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=[a];break;case s.ArrayExpression:d=!0;c=[];q(a.elements,function(a){aa(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=c;break;case s.ObjectExpression:d=
+!0;c=[];q(a.properties,function(a){aa(a.value,b);d=d&&a.value.constant;a.value.constant||c.push.apply(c,a.value.toWatch)});a.constant=d;a.toWatch=c;break;case s.ThisExpression:a.constant=!1;a.toWatch=[];break;case s.LocalsExpression:a.constant=!1,a.toWatch=[]}}function nd(a){if(1==a.length){a=a[0].expression;var b=a.toWatch;return 1!==b.length?b:b[0]!==a?b:u}}function od(a){return a.type===s.Identifier||a.type===s.MemberExpression}function pd(a){if(1===a.body.length&&od(a.body[0].expression))return{type:s.AssignmentExpression,
+left:a.body[0].expression,right:{type:s.NGValueParameter},operator:"="}}function qd(a){return 0===a.body.length||1===a.body.length&&(a.body[0].expression.type===s.Literal||a.body[0].expression.type===s.ArrayExpression||a.body[0].expression.type===s.ObjectExpression)}function rd(a,b){this.astBuilder=a;this.$filter=b}function sd(a,b){this.astBuilder=a;this.$filter=b}function Ib(a){return"constructor"==a}function fc(a){return D(a.valueOf)?a.valueOf():jg.call(a)}function rf(){var a=V(),b=V(),d={"true":!0,
+"false":!1,"null":null,undefined:u};this.addLiteral=function(a,b){d[a]=b};this.$get=["$filter",function(c){function e(d,e,g){var p,t,G;g=g||x;switch(typeof d){case "string":G=d=d.trim();var C=g?b:a;p=C[G];if(!p){":"===d.charAt(0)&&":"===d.charAt(1)&&(t=!0,d=d.substring(2));p=g?L:F;var K=new gc(p);p=(new hc(K,c,p)).parse(d);p.constant?p.$$watchDelegate=m:t?p.$$watchDelegate=p.literal?l:k:p.inputs&&(p.$$watchDelegate=h);g&&(p=f(p));C[G]=p}return n(p,e);case "function":return n(d,e);default:return n(E,
+e)}}function f(a){function b(c,d,e,f){var g=x;x=!0;try{return a(c,d,e,f)}finally{x=g}}if(!a)return a;b.$$watchDelegate=a.$$watchDelegate;b.assign=f(a.assign);b.constant=a.constant;b.literal=a.literal;for(var c=0;a.inputs&&c=this.promise.$$state.status&&
+d&&d.length&&a(function(){for(var a,e,f=0,g=d.length;f
+a)for(b in l++,f)va.call(e,b)||(v--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,h,k=1t&&(z=4-t,A[z]||(A[z]=[]),A[z].push({msg:D(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,newVal:g,oldVal:k}));else if(a===c){q=!1;break a}}catch(H){f(H)}if(!(p=F.$$watchersCount&&
+F.$$childHead||F!==this&&F.$$nextSibling))for(;F!==this&&!(p=F.$$nextSibling);)F=F.$parent}while(F=p);if((q||v.length)&&!t--)throw w.$$phase=null,d("infdig",b,A);}while(q||v.length);for(w.$$phase=null;u.length;)try{u.shift()()}catch(J){f(J)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this===w&&h.$$applicationDestroyed();p(this,-this.$$watchersCount);for(var b in this.$$listenerCount)F(this,this.$$listenerCount[b],b);a&&a.$$childHead==
+this&&(a.$$childHead=this.$$nextSibling);a&&a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=E;this.$on=this.$watch=this.$watchGroup=function(){return E};this.$$listeners={};this.$$nextSibling=null;l(this)}},$eval:function(a,b){return g(a)(this,b)},$evalAsync:function(a,b){w.$$phase||
+v.length||h.defer(function(){v.length&&w.$digest()});v.push({scope:this,expression:g(a),locals:b})},$$postDigest:function(a){u.push(a)},$apply:function(a){try{n("$apply");try{return this.$eval(a)}finally{w.$$phase=null}}catch(b){f(b)}finally{try{w.$digest()}catch(c){throw f(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&t.push(b);a=g(a);r()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=
+0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(c[d]=null,F(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,g=!1,h={name:a,targetScope:e,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=cb([h],arguments,1),l,m;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(m=d.length;lDa)throw ua("iequirks");var c=ia(ma);c.isEnabled=function(){return a};c.trustAs=d.trustAs;c.getTrusted=d.getTrusted;c.valueOf=d.valueOf;a||(c.trustAs=c.getTrusted=function(a,b){return b},
+c.valueOf=$a);c.parseAs=function(a,d){var e=b(d);return e.literal&&e.constant?e:b(d,function(b){return c.getTrusted(a,b)})};var e=c.parseAs,f=c.getTrusted,g=c.trustAs;q(ma,function(a,b){var d=N(b);c[fb("parse_as_"+d)]=function(b){return e(a,b)};c[fb("get_trusted_"+d)]=function(b){return f(a,b)};c[fb("trust_as_"+d)]=function(b){return g(a,b)}});return c}]}function xf(){this.$get=["$window","$document",function(a,b){var d={},c=!(a.chrome&&a.chrome.app&&a.chrome.app.runtime)&&a.history&&a.history.pushState,
+e=Y((/android (\d+)/.exec(N((a.navigator||{}).userAgent))||[])[1]),f=/Boxee/i.test((a.navigator||{}).userAgent),g=b[0]||{},h,k=/^(Moz|webkit|ms)(?=[A-Z])/,l=g.body&&g.body.style,m=!1,n=!1;if(l){for(var p in l)if(m=k.exec(p)){h=m[0];h=h.substr(0,1).toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in l&&"webkit");m=!!("transition"in l||h+"Transition"in l);n=!!("animation"in l||h+"Animation"in l);!e||m&&n||(m=y(l.webkitTransition),n=y(l.webkitAnimation))}return{history:!(!c||4>e||f),hasEvent:function(a){if("input"===
+a&&11>=Da)return!1;if(z(d[a])){var b=g.createElement("div");d[a]="on"+a in b}return d[a]},csp:Ga(),vendorPrefix:h,transitions:m,animations:n,android:e}}]}function zf(){var a;this.httpOptions=function(b){return b?(a=b,this):a};this.$get=["$templateCache","$http","$q","$sce",function(b,d,c,e){function f(g,h){f.totalPendingRequests++;y(g)&&b.get(g)||(g=e.getTrustedResourceUrl(g));var k=d.defaults&&d.defaults.transformResponse;M(k)?k=k.filter(function(a){return a!==ac}):k===ac&&(k=null);return d.get(g,
+S({cache:b,transformResponse:k},a))["finally"](function(){f.totalPendingRequests--}).then(function(a){b.put(g,a.data);return a.data},function(a){if(!h)throw lg("tpload",g,a.status,a.statusText);return c.reject(a)})}f.totalPendingRequests=0;return f}]}function Af(){this.$get=["$rootScope","$browser","$location",function(a,b,d){return{findBindings:function(a,b,d){a=a.getElementsByClassName("ng-binding");var g=[];q(a,function(a){var c=ea.element(a).data("$binding");c&&q(c,function(c){d?(new RegExp("(^|\\s)"+
+ud(b)+"(\\s|\\||$)")).test(c)&&g.push(a):-1!=c.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,d){for(var g=["ng-","data-ng-","ng\\:"],h=0;hc&&(c=e),c+=+a.slice(e+1),a=a.substring(0,e)):0>c&&(c=a.length);for(e=0;a.charAt(e)==jc;e++);if(e==(g=a.length))d=[0],c=1;else{for(g--;a.charAt(g)==jc;)g--;c-=e;d=[];for(f=0;e<=g;e++,f++)d[f]=+a.charAt(e)}c>Ed&&(d=d.splice(0,Ed-1),b=c-1,c=1);return{d:d,e:b,i:c}}function tg(a,b,d,c){var e=a.d,f=e.length-a.i;b=z(b)?Math.min(Math.max(d,f),c):+b;d=b+a.i;c=e[d];if(0d-1){for(c=0;c>d;c--)e.unshift(0),a.i++;e.unshift(1);a.i++}else e[d-1]++;for(;fh;)k.unshift(0),h++;0=b.lgSize&&h.unshift(k.splice(-b.lgSize).join(""));k.length>b.gSize;)h.unshift(k.splice(-b.gSize).join(""));k.length&&h.unshift(k.join(""));k=h.join(d);f.length&&(k+=c+f.join(""));e&&(k+="e+"+e)}return 0>a&&!g?b.negPre+k+b.negSuf:b.posPre+k+b.posSuf}function Jb(a,b,d,c){var e="";if(0>a||c&&0>=a)c?a=-a+1:(a=-a,e="-");for(a=""+a;a.length-d)f+=d;0===f&&-12==d&&(f=12);return Jb(f,b,c,e)}}function lb(a,b,d){return function(c,e){var f=c["get"+a](),g=vb((d?"STANDALONE":"")+(b?"SHORT":"")+a);return e[g][f]}}function Fd(a){var b=(new Date(a,0,1)).getDay();return new Date(a,0,(4>=b?5:12)-b)}function Gd(a){return function(b){var d=Fd(b.getFullYear());b=+new Date(b.getFullYear(),b.getMonth(),b.getDate()+(4-b.getDay()))-+d;b=1+Math.round(b/6048E5);return Jb(b,a)}}function kc(a,b){return 0>=a.getFullYear()?
+b.ERAS[0]:b.ERAS[1]}function zd(a){function b(a){var b;if(b=a.match(d)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,k=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=Y(b[9]+b[10]),g=Y(b[9]+b[11]));h.call(a,Y(b[1]),Y(b[2])-1,Y(b[3]));f=Y(b[4]||0)-f;g=Y(b[5]||0)-g;h=Y(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));k.call(a,f,g,h,b)}return a}var d=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,d,f){var g="",h=
+[],k,l;d=d||"mediumDate";d=a.DATETIME_FORMATS[d]||d;y(c)&&(c=ug.test(c)?Y(c):b(c));R(c)&&(c=new Date(c));if(!fa(c)||!isFinite(c.getTime()))return c;for(;d;)(l=vg.exec(d))?(h=cb(h,l,1),d=h.pop()):(h.push(d),d=null);var m=c.getTimezoneOffset();f&&(m=vc(f,m),c=Rb(c,f,!0));q(h,function(b){k=wg[b];g+=k?k(c,a.DATETIME_FORMATS,m):"''"===b?"'":b.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function ng(){return function(a,b){z(b)&&(b=2);return db(a,b)}}function og(){return function(a,b,d){b=Infinity===
+Math.abs(Number(b))?Number(b):Y(b);if(isNaN(b))return a;R(a)&&(a=a.toString());if(!M(a)&&!y(a))return a;d=!d||isNaN(d)?0:Y(d);d=0>d?Math.max(0,a.length+d):d;return 0<=b?a.slice(d,d+b):0===d?a.slice(b,a.length):a.slice(Math.max(0,d+b),d)}}function Bd(a){function b(b,d){d=d?-1:1;return b.map(function(b){var c=1,h=$a;if(D(b))h=b;else if(y(b)){if("+"==b.charAt(0)||"-"==b.charAt(0))c="-"==b.charAt(0)?-1:1,b=b.substring(1);if(""!==b&&(h=a(b),h.constant))var k=h(),h=function(a){return a[k]}}return{get:h,
+descending:c*d}})}function d(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}return function(a,e,f){if(null==a)return a;if(!za(a))throw O("orderBy")("notarray",a);M(e)||(e=[e]);0===e.length&&(e=["+"]);var g=b(e,f);g.push({get:function(){return{}},descending:f?-1:1});a=Array.prototype.map.call(a,function(a,b){return{value:a,predicateValues:g.map(function(c){var e=c.get(a);c=typeof e;if(null===e)c="string",e="null";else if("string"===c)e=e.toLowerCase();else if("object"===
+c)a:{if("function"===typeof e.valueOf&&(e=e.valueOf(),d(e)))break a;if(rc(e)&&(e=e.toString(),d(e)))break a;e=b}return{value:e,type:c}})}});a.sort(function(a,b){for(var c=0,d=0,e=g.length;db||37<=b&&40>=b||
+m(a,this,this.value)});if(e.hasEvent("paste"))b.on("paste cut",m)}b.on("change",l);if(Jd[g]&&c.$$hasNativeValidators&&g===d.type)b.on("keydown wheel mousedown",function(a){if(!k){var b=this.validity,c=b.badInput,d=b.typeMismatch;k=f.defer(function(){k=null;b.badInput===c&&b.typeMismatch===d||l(a)})}});c.$render=function(){var a=c.$isEmpty(c.$viewValue)?"":c.$viewValue;b.val()!==a&&b.val(a)}}function Mb(a,b){return function(d,c){var e,f;if(fa(d))return d;if(y(d)){'"'==d.charAt(0)&&'"'==d.charAt(d.length-
+1)&&(d=d.substring(1,d.length-1));if(xg.test(d))return new Date(d);a.lastIndex=0;if(e=a.exec(d))return e.shift(),f=c?{yyyy:c.getFullYear(),MM:c.getMonth()+1,dd:c.getDate(),HH:c.getHours(),mm:c.getMinutes(),ss:c.getSeconds(),sss:c.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},q(e,function(a,c){c=x};g.$observe("min",function(a){x=
+p(a);h.$validate()})}if(A(g.max)||g.ngMax){var r;h.$validators.max=function(a){return!n(a)||z(r)||d(a)<=r};g.$observe("max",function(a){r=p(a);h.$validate()})}}}function Kd(a,b,d,c){(c.$$hasNativeValidators=J(b[0].validity))&&c.$parsers.push(function(a){var c=b.prop("validity")||{};return c.badInput||c.typeMismatch?u:a})}function Ld(a,b,d,c,e){if(A(c)){a=a(c);if(!a.constant)throw ob("constexpr",d,c);return a(b)}return e}function mc(a,b){a="ngClass"+a;return["$animate",function(d){function c(a,b){var c=
+[],d=0;a:for(;d(?:<\/\1>|)$/,Ub=/<|?\w+;/,Jf=/<([\w:-]+)/,Kf=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,ha={option:[1,'"],thead:[1,"
- {{end}}
-
-See also
-
-http://beego.me/docs/mvc/view/page.md
-
-*/
-package pagination
diff --git a/vendor/github.com/astaxie/beego/utils/pagination/paginator.go b/vendor/github.com/astaxie/beego/utils/pagination/paginator.go
deleted file mode 100644
index c6db31e08..000000000
--- a/vendor/github.com/astaxie/beego/utils/pagination/paginator.go
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright 2014 beego Author. All Rights Reserved.
-//
-// Licensed 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.
-
-package pagination
-
-import (
- "math"
- "net/http"
- "net/url"
- "strconv"
-)
-
-// Paginator within the state of a http request.
-type Paginator struct {
- Request *http.Request
- PerPageNums int
- MaxPages int
-
- nums int64
- pageRange []int
- pageNums int
- page int
-}
-
-// PageNums Returns the total number of pages.
-func (p *Paginator) PageNums() int {
- if p.pageNums != 0 {
- return p.pageNums
- }
- pageNums := math.Ceil(float64(p.nums) / float64(p.PerPageNums))
- if p.MaxPages > 0 {
- pageNums = math.Min(pageNums, float64(p.MaxPages))
- }
- p.pageNums = int(pageNums)
- return p.pageNums
-}
-
-// Nums Returns the total number of items (e.g. from doing SQL count).
-func (p *Paginator) Nums() int64 {
- return p.nums
-}
-
-// SetNums Sets the total number of items.
-func (p *Paginator) SetNums(nums interface{}) {
- p.nums, _ = toInt64(nums)
-}
-
-// Page Returns the current page.
-func (p *Paginator) Page() int {
- if p.page != 0 {
- return p.page
- }
- if p.Request.Form == nil {
- p.Request.ParseForm()
- }
- p.page, _ = strconv.Atoi(p.Request.Form.Get("p"))
- if p.page > p.PageNums() {
- p.page = p.PageNums()
- }
- if p.page <= 0 {
- p.page = 1
- }
- return p.page
-}
-
-// Pages Returns a list of all pages.
-//
-// Usage (in a view template):
-//
-// {{range $index, $page := .paginator.Pages}}
-//
\ No newline at end of file
diff --git a/views/change-password.htm b/views/change-password.htm
new file mode 100644
index 000000000..6526fb7e9
--- /dev/null
+++ b/views/change-password.htm
@@ -0,0 +1,77 @@
+
+
-
-
\ No newline at end of file
diff --git a/views/dashboard.htm b/views/dashboard.htm
new file mode 100644
index 000000000..3affdcea3
--- /dev/null
+++ b/views/dashboard.htm
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// 'logs' | tr //
+
+
+
+
+
+
\ No newline at end of file
diff --git a/views/forgot-password.htm b/views/forgot-password.htm
new file mode 100644
index 000000000..389c981b1
--- /dev/null
+++ b/views/forgot-password.htm
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
// 'forgot_password' | tr //
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/views/forgot-password.tpl b/views/forgot-password.tpl
deleted file mode 100644
index 0a2738479..000000000
--- a/views/forgot-password.tpl
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
{{i18n .Lang "title_forgot_password"}}
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/views/index.htm b/views/index.htm
new file mode 100644
index 000000000..9659b01d2
--- /dev/null
+++ b/views/index.htm
@@ -0,0 +1,74 @@
+
+
\ No newline at end of file
diff --git a/views/navigation-header.htm b/views/navigation-header.htm
new file mode 100644
index 000000000..5a4e9bf08
--- /dev/null
+++ b/views/navigation-header.htm
@@ -0,0 +1,23 @@
+
+{{ if eq .HasLoggedIn true }}
+
+{{ end }}
\ No newline at end of file
diff --git a/views/optional-menu.htm b/views/optional-menu.htm
new file mode 100644
index 000000000..6950fe206
--- /dev/null
+++ b/views/optional-menu.htm
@@ -0,0 +1,44 @@
+
+{{if eq .HasLoggedIn true }}
+
+{{ end }}
\ No newline at end of file
diff --git a/views/project.htm b/views/project.htm
new file mode 100644
index 000000000..d5bfb3122
--- /dev/null
+++ b/views/project.htm
@@ -0,0 +1,75 @@
+
+
-
-
\ No newline at end of file
diff --git a/views/repository.htm b/views/repository.htm
new file mode 100644
index 000000000..5248b7430
--- /dev/null
+++ b/views/repository.htm
@@ -0,0 +1,47 @@
+
+
+ {{.URL}}/reset_password?reset_uuid={{.UUID}}
+
\ No newline at end of file
diff --git a/views/reset-password.htm b/views/reset-password.htm
new file mode 100644
index 000000000..3da84e189
--- /dev/null
+++ b/views/reset-password.htm
@@ -0,0 +1,64 @@
+
+
\ No newline at end of file
diff --git a/views/reset-password.tpl b/views/reset-password.tpl
deleted file mode 100644
index e98a5c27b..000000000
--- a/views/reset-password.tpl
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
{{i18n .Lang "title_reset_password"}}
-
-
-
-
-
-
-
-
{{i18n .Lang "password_description"}}
-
-
-
-
-
-
{{i18n .Lang "password_description"}}
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/views/search.htm b/views/search.htm
new file mode 100644
index 000000000..7676c4b4e
--- /dev/null
+++ b/views/search.htm
@@ -0,0 +1,40 @@
+
+
\ No newline at end of file
diff --git a/views/sign-in.htm b/views/sign-in.htm
new file mode 100644
index 000000000..a4bd94975
--- /dev/null
+++ b/views/sign-in.htm
@@ -0,0 +1,65 @@
+
+{{ if eq .HasLoggedIn true }}
+
+
// 'welcome' | tr //
+
+
+
+
+{{ else }}
+
+
+
+
+
+
+ // 'username_is_required' | tr //
+
+
+
+
+
+
+
+
+
+ // 'password_is_required' | tr //
+
+ // vm.errorMessage | tr //
+
+
+
+
+
+
+
+ {{ if eq .AuthMode "db_auth" }}
+
+ {{ end }}
+
+{{ end }}
\ No newline at end of file
diff --git a/views/sign-in.tpl b/views/sign-in.tpl
deleted file mode 100644
index 86723ad01..000000000
--- a/views/sign-in.tpl
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ if eq .AuthMode "db_auth" }}
-
-
-
-
-
- {{ end }}
-
-
-
-
\ No newline at end of file
diff --git a/views/sign-up.htm b/views/sign-up.htm
new file mode 100644
index 000000000..4bae1a5d0
--- /dev/null
+++ b/views/sign-up.htm
@@ -0,0 +1,126 @@
+
+