Merge remote-tracking branch 'origin/master' into swaggerlikeus

Conflicts:
	data/database.py
	endpoints/api.py
	endpoints/common.py
	templates/base.html
	test/data/test.db
	test/specs.py
This commit is contained in:
jakedt 2014-03-19 15:39:44 -04:00
commit c93c62600d
59 changed files with 4636 additions and 216 deletions

View file

@ -102,9 +102,88 @@ function getMarkedDown(string) {
return Markdown.getSanitizingConverter().makeHtml(string || '');
}
quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'angular-tour', 'restangular', 'angularMoment', 'angulartics', /*'angulartics.google.analytics',*/ 'angulartics.mixpanel', '$strap.directives', 'ngCookies', 'ngSanitize', 'angular-md5', 'pasvaz.bindonce', 'ansiToHtml', 'ngAnimate'], function($provide, cfpLoadingBarProvider) {
quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'angular-tour', 'restangular', 'angularMoment', 'angulartics', /*'angulartics.google.analytics',*/ 'angulartics.mixpanel', 'mgcrea.ngStrap', 'ngCookies', 'ngSanitize', 'angular-md5', 'pasvaz.bindonce', 'ansiToHtml', 'ngAnimate'], function($provide, cfpLoadingBarProvider) {
cfpLoadingBarProvider.includeSpinner = false;
/**
* Specialized wrapper around array which provides a toggle() method for viewing the contents of the
* array in a manner that is asynchronously filled in over a short time period. This prevents long
* pauses in the UI for ngRepeat's when the array is significant in size.
*/
$provide.factory('AngularViewArray', ['$interval', function($interval) {
var ADDTIONAL_COUNT = 50;
function _ViewArray() {
this.isVisible = false;
this.visibleEntries = null;
this.hasEntries = false;
this.entries = [];
this.timerRef_ = null;
this.currentIndex_ = 0;
}
_ViewArray.prototype.push = function(elem) {
this.entries.push(elem);
this.hasEntries = true;
if (this.isVisible) {
this.setVisible(true);
}
};
_ViewArray.prototype.toggle = function() {
this.setVisible(!this.isVisible);
};
_ViewArray.prototype.setVisible = function(newState) {
this.isVisible = newState;
this.visibleEntries = [];
this.currentIndex_ = 0;
if (newState) {
this.showAdditionalEntries_();
this.startTimer_();
} else {
this.stopTimer_();
}
};
_ViewArray.prototype.showAdditionalEntries_ = function() {
var i = 0;
for (i = this.currentIndex_; i < (this.currentIndex_ + ADDTIONAL_COUNT) && i < this.entries.length; ++i) {
this.visibleEntries.push(this.entries[i]);
}
this.currentIndex_ = i;
if (this.currentIndex_ >= this.entries.length) {
this.stopTimer_();
}
};
_ViewArray.prototype.startTimer_ = function() {
var that = this;
this.timerRef_ = $interval(function() {
that.showAdditionalEntries_();
}, 10);
};
_ViewArray.prototype.stopTimer_ = function() {
if (this.timerRef_) {
$interval.cancel(this.timerRef_);
this.timerRef_ = null;
}
};
var service = {
'create': function() {
return new _ViewArray();
}
};
return service;
}]);
$provide.factory('UtilService', ['$sanitize', function($sanitize) {
var utilService = {};
@ -143,6 +222,49 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'angu
return builderService;
}]);
$provide.factory('StringBuilderService', ['$sce', function($sce) {
var stringBuilderService = {};
stringBuilderService.buildString = function(value_or_func, metadata) {
var fieldIcons = {
'username': 'user',
'activating_username': 'user',
'delegate_user': 'user',
'delegate_team': 'group',
'team': 'group',
'token': 'key',
'repo': 'hdd-o',
'robot': 'wrench',
'tag': 'tag',
'role': 'th-large',
'original_role': 'th-large'
};
var description = value_or_func;
if (typeof description != 'string') {
description = description(metadata);
}
for (var key in metadata) {
if (metadata.hasOwnProperty(key)) {
var value = metadata[key] != null ? metadata[key].toString() : '(Unknown)';
var markedDown = getMarkedDown(value);
markedDown = markedDown.substr('<p>'.length, markedDown.length - '<p></p>'.length);
var icon = fieldIcons[key];
if (icon) {
markedDown = '<i class="fa fa-' + icon + '"></i>' + markedDown;
}
description = description.replace('{' + key + '}', '<code>' + markedDown + '</code>');
}
}
return $sce.trustAsHtml(description.replace('\n', '<br>'));
};
return stringBuilderService;
}]);
$provide.factory('ImageMetadataService', ['UtilService', function(UtilService) {
var metadataService = {};
@ -360,7 +482,6 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'angu
anonymous: true,
username: null,
email: null,
askForPassword: false,
organizations: [],
logins: []
}
@ -467,6 +588,101 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'angu
return userService;
}]);
$provide.factory('NotificationService', ['$rootScope', '$interval', 'UserService', 'ApiService', 'StringBuilderService', 'PlanService', 'UserService',
function($rootScope, $interval, UserService, ApiService, StringBuilderService, PlanService, UserService) {
var notificationService = {
'user': null,
'notifications': [],
'notificationClasses': [],
'notificationSummaries': []
};
var pollTimerHandle = null;
var notificationKinds = {
'test_notification': {
'level': 'primary',
'message': 'This notification is a long message for testing',
'page': '/about/'
},
'password_required': {
'level': 'error',
'message': 'In order to begin pushing and pulling repositories to Quay.io, a password must be set for your account',
'page': '/user?tab=password'
},
'over_private_usage': {
'level': 'error',
'message': 'Namespace {namespace} is over its allowed private repository count. ' +
'<br><br>Please upgrade your plan to avoid disruptions in service.',
'page': function(metadata) {
var organization = UserService.getOrganization(metadata['namespace']);
if (organization) {
return '/organization/' + metadata['namespace'] + '/admin';
} else {
return '/user';
}
}
}
};
notificationService.getPage = function(notification) {
var page = notificationKinds[notification['kind']]['page'];
if (typeof page != 'string') {
page = page(notification['metadata']);
}
return page;
};
notificationService.getMessage = function(notification) {
var kindInfo = notificationKinds[notification['kind']];
return StringBuilderService.buildString(kindInfo['message'], notification['metadata']);
};
notificationService.getClass = function(notification) {
return 'notification-' + notificationKinds[notification['kind']]['level'];
};
notificationService.getClasses = function(notifications) {
var classes = [];
for (var i = 0; i < notifications.length; ++i) {
var notification = notifications[i];
classes.push(notificationService.getClass(notification));
}
return classes.join(' ');
};
notificationService.update = function() {
var user = UserService.currentUser();
if (!user || user.anonymous) {
return;
}
ApiService.listUserNotifications().then(function(resp) {
notificationService.notifications = resp['notifications'];
notificationService.notificationClasses = notificationService.getClasses(notificationService.notifications);
});
};
notificationService.reset = function() {
$interval.cancel(pollTimerHandle);
pollTimerHandle = $interval(notificationService.update, 5 * 60 * 1000 /* five minutes */);
};
// Watch for plan changes and update.
PlanService.registerListener(this, function(plan) {
notificationService.reset();
notificationService.update();
});
// Watch for user changes and update.
$rootScope.$watch(function() { return UserService.currentUser(); }, function(currentUser) {
notificationService.reset();
notificationService.update();
});
return notificationService;
}]);
$provide.factory('KeyService', ['$location', function($location) {
var keyService = {}
@ -1405,7 +1621,7 @@ quayApp.directive('logsView', function () {
'repository': '=repository',
'performer': '=performer'
},
controller: function($scope, $element, $sce, Restangular, ApiService, TriggerDescriptionBuilder) {
controller: function($scope, $element, $sce, Restangular, ApiService, TriggerDescriptionBuilder, StringBuilderService) {
$scope.loading = true;
$scope.logs = null;
$scope.kindsAllowed = null;
@ -1620,43 +1836,9 @@ quayApp.directive('logsView', function () {
return $scope.chart.getColor(kind);
};
$scope.getDescription = function(log) {
var fieldIcons = {
'username': 'user',
'activating_username': 'user',
'delegate_user': 'user',
'delegate_team': 'group',
'team': 'group',
'token': 'key',
'repo': 'hdd-o',
'robot': 'wrench',
'tag': 'tag',
'role': 'th-large',
'original_role': 'th-large'
};
$scope.getDescription = function(log) {
log.metadata['_ip'] = log.ip ? log.ip : null;
var description = logDescriptions[log.kind] || log.kind;
if (typeof description != 'string') {
description = description(log.metadata);
}
for (var key in log.metadata) {
if (log.metadata.hasOwnProperty(key)) {
var value = log.metadata[key] != null ? log.metadata[key].toString() : '(Unknown)';
var markedDown = getMarkedDown(value);
markedDown = markedDown.substr('<p>'.length, markedDown.length - '<p></p>'.length);
var icon = fieldIcons[key];
if (icon) {
markedDown = '<i class="fa fa-' + icon + '"></i>' + markedDown;
}
description = description.replace('{' + key + '}', '<code>' + markedDown + '</code>');
}
}
return $sce.trustAsHtml(description.replace('\n', '<br>'));
return StringBuilderService.buildString(logDescriptions[log.kind] || log.kind, log.metadata);
};
$scope.$watch('organization', update);
@ -1921,6 +2103,31 @@ quayApp.directive('prototypeManager', function () {
});
quayApp.directive('deleteUi', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/delete-ui.html',
replace: false,
transclude: true,
restrict: 'C',
scope: {
'deleteTitle': '=deleteTitle',
'buttonTitle': '=buttonTitle',
'performDelete': '&performDelete'
},
controller: function($scope, $element) {
$scope.buttonTitleInternal = $scope.buttonTitle || 'Delete';
$element.children().attr('tabindex', 0);
$scope.focus = function() {
$element[0].firstChild.focus();
};
}
};
return directiveDefinitionObject;
});
quayApp.directive('popupInputButton', function () {
var directiveDefinitionObject = {
priority: 0,
@ -1939,7 +2146,7 @@ quayApp.directive('popupInputButton', function () {
var box = $('#input-box');
box[0].value = '';
box.focus();
}, 10);
}, 40);
};
$scope.getRegexp = function(pattern) {
@ -2153,26 +2360,12 @@ quayApp.directive('headerBar', function () {
restrict: 'C',
scope: {
},
controller: function($scope, $element, $location, UserService, PlanService, ApiService) {
$scope.overPlan = false;
var checkOverPlan = function() {
if ($scope.user.anonymous) {
$scope.overPlan = false;
return;
}
ApiService.getUserPrivateAllowed().then(function(resp) {
$scope.overPlan = !resp['privateAllowed'];
});
};
// Monitor any user changes and place the current user into the scope.
UserService.updateUserIn($scope, checkOverPlan);
controller: function($scope, $element, $location, UserService, PlanService, ApiService, NotificationService) {
$scope.notificationService = NotificationService;
// Monitor any user changes and place the current user into the scope.
UserService.updateUserIn($scope);
// Monitor any plan changes.
PlanService.registerListener(this, checkOverPlan);
$scope.signout = function() {
ApiService.logout().then(function() {
UserService.load();
@ -3314,6 +3507,54 @@ quayApp.directive('buildProgress', function () {
});
quayApp.directive('notificationView', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/notification-view.html',
replace: false,
transclude: false,
restrict: 'C',
scope: {
'notification': '=notification',
'parent': '=parent'
},
controller: function($scope, $element, $location, UserService, NotificationService) {
$scope.getMessage = function(notification) {
return NotificationService.getMessage(notification);
};
$scope.getGravatar = function(orgname) {
var organization = UserService.getOrganization(orgname);
return organization['gravatar'] || '';
};
$scope.parseDate = function(dateString) {
return Date.parse(dateString);
};
$scope.showNotification = function() {
var url = NotificationService.getPage($scope.notification);
if (url) {
var parts = url.split('?')
$location.path(parts[0]);
if (parts.length > 1) {
$location.search(parts[1]);
}
$scope.parent.$hide();
}
};
$scope.getClass = function(notification) {
return NotificationService.getClass(notification);
};
}
};
return directiveDefinitionObject;
});
quayApp.directive('dockerfileBuildDialog', function () {
var directiveDefinitionObject = {
priority: 0,
@ -3594,6 +3835,11 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanServi
}
}
if (response.status == 500) {
document.location = '/500';
return false;
}
return true;
});