Add UI for viewing labels on a manifest in the tags view

This commit is contained in:
Joseph Schorr 2017-03-08 16:25:14 -05:00
parent 69e476b1f4
commit f17ef4adda
13 changed files with 336 additions and 112 deletions

View file

@ -510,6 +510,7 @@ def populate_database(minimal=False, with_storage=False):
first_label = model.label.create_manifest_label(tag_manifest, 'foo', 'bar', 'manifest')
model.label.create_manifest_label(tag_manifest, 'foo', 'baz', 'api')
model.label.create_manifest_label(tag_manifest, 'anotherlabel', '1234', 'internal')
model.label.create_manifest_label(tag_manifest, 'jsonlabel', '{"hey": "there"}', 'internal')
label_metadata = {
'key': 'foo',

View file

@ -127,11 +127,12 @@
color: black;
}
.arepo-panel-tags-element .tag-span {
overflow: hidden;
text-overflow: ellipsis;
max-width: 250px;
display: inline-block;
.repo-panel-tags-element tr.expanded-view td {
border-bottom: 0px;
}
.repo-panel-tags-element .labels-col {
padding-top: 0px;
}
@media (max-width: 1000px) {

View file

@ -0,0 +1,9 @@
.label-list-element .label-view {
margin-right: 6px;
margin-bottom: 4px;
}
.label-list-element .empty-list {
font-size: 12px;
color: #ccc;
}

View file

@ -0,0 +1,25 @@
.label-view-element {
display: inline-block;
padding: 1px;
border-radius: 9px;
background-color: #eee;
padding-left: 8px;
padding-right: 8px;
font-size: 12px;
cursor: default;
}
.label-view-element .kind {
text-transform: uppercase;
font-size: 8px;
color: #aaa;
margin-right: 2px;
position: relative;
top: -1px;
}
.label-view-element .value {
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
}

View file

@ -0,0 +1,8 @@
.manifest-label-list-element {
padding-left: 6px;
}
.manifest-label-list-element .none {
font-size: 12px;
color: #ccc;
}

View file

@ -0,0 +1,10 @@
<div class="label-list-element">
<div class="label-view"
ng-repeat="label in labels"
expand="{{expand}}"
label="label">
</div>
<div class="empty-list" ng-if="!labels.length">
No labels found
</div>
</div>

View file

@ -0,0 +1,8 @@
<a class="label-view-element" ng-click="viewLabelValue()">
<span class="kind">{{ getKind(label) }}</span>
<span class="label-value">
<span class="key">{{ label.key }}</span>
<span class="equals">=</span>
<span class="value">{{ label.value }}</span>
</span>
</a>

View file

@ -0,0 +1,11 @@
<div class="manifest-label-list-element">
<div class="cor-loader-inline" ng-if="repository && manifestDigest && !labels && !loadError"></div>
<div class="none" ng-if="repository && !manifestDigest && !loadError">
This tag does not have an associated manifest
</div>
<div class="none" ng-if="repository && manifestDigest && loadError">
Could not load labels for this manifest
</div>
<div class="label-list" labels="labels"
ng-if="repository && manifestDigest && labels && !loadError"></div>
</div>

View file

@ -1,4 +1,17 @@
<div class="repo-panel-tags-element">
<div class="tab-header-controls">
<div class="btn-group btn-group-sm">
<button class="btn" ng-class="!expandedView ? 'btn-primary active' : 'btn-default'"
ng-click="setExpanded(false)">
Compact
</button>
<button class="btn" ng-class="expandedView ? 'btn-info active' : 'btn-default'"
ng-click="setExpanded(true)">
Expanded
</button>
</div>
</div>
<h3 class="tab-header"><span class="hidden-xs">Repository </span>Tags</h3>
<div class="co-alert co-alert-danger" ng-if="hasDefcon1">
One or more of your tags has an <strong>extremely critical</strong> vulnerability which should be addressed immediately:
@ -103,10 +116,11 @@
<td class="hidden-xs hidden-sm" style="width: 4px"></td>
</thead>
<tr class="co-checkable-row"
<tbody class="co-checkable-row"
ng-repeat="tag in tags | slice:(tagsPerPage * options.page):(tagsPerPage * (options.page + 1))"
ng-class="checkedTags.isChecked(tag, checkedTags.checked) ? 'checked' : ''"
bindonce>
<tr ng-class="expandedView ? 'expanded-view': ''">
<td><span class="cor-checkable-item" controller="checkedTags" item="tag"></span></td>
<td class="co-flowing-col"><span class="tag-span"><span bo-text="tag.name"></span></span></td>
<td class="hidden-xs">
@ -226,6 +240,23 @@
</td>
<td class="options-col hidden-xs hidden-sm"><!-- Whitespace col --></td>
</tr>
<tr ng-if="expandedView">
<td class="checkbox-col"></td>
<td class="labels-col" colspan="{{5 + (Features.SECURITY_SCANNER ? 1 : 0)}}">
<div class="manifest-label-list" repository="repository"
manifest-digest="tag.manifest_digest"></div>
</td>
<td class="hidden-xs hidden-sm image-track" ng-repeat="it in imageTracks"
ng-if="imageTracks.length <= maxTrackCount" bindonce>
<span ng-repeat="entry in it.entries">
<span class="image-track-line"
bo-class="trackLineExpandedClass($parent.$parent.$parent.$index, entry)"
bo-style="{'borderColor': entry.color}"></span>
</span>
</td>
<td colspan="2"></td>
</tr>
</tbody>
</table>
<div class="empty" ng-if="allTags.length && !tags.length">

View file

@ -35,6 +35,8 @@ angular.module('quay').directive('repoPanelTags', function () {
$scope.tagActionHandler = null;
$scope.tagsPerPage = 25;
$scope.expandedView = false;
$scope.imageVulnerabilities = {};
$scope.defcon1 = {};
$scope.hasDefcon1 = false;
@ -262,6 +264,26 @@ angular.module('quay').directive('repoPanelTags', function () {
$scope.checkedTags.setChecked($scope.tags);
};
$scope.trackLineExpandedClass = function(index, track_info) {
var startIndex = $.inArray(track_info.tags[0], $scope.tags);
var endIndex = $.inArray(track_info.tags[track_info.tags.length - 1], $scope.tags);
index += $scope.options.page * $scope.tagsPerPage;
if (index < startIndex) {
return 'before';
}
if (index > endIndex) {
return 'after';
}
if (index >= startIndex && index < endIndex) {
return 'middle';
}
return '';
};
$scope.trackLineClass = function(index, track_info) {
var startIndex = $.inArray(track_info.tags[0], $scope.tags);
var endIndex = $.inArray(track_info.tags[track_info.tags.length - 1], $scope.tags);
@ -359,6 +381,10 @@ angular.module('quay').directive('repoPanelTags', function () {
$scope.setTab('history');
};
$scope.setExpanded = function(expanded) {
$scope.expandedView = expanded;
};
$scope.getTagNames = function(checked) {
var names = checked.map(function(tag) {
return tag.name;

View file

@ -0,0 +1,15 @@
/**
* An element which displays labels.
*/
angular.module('quay').directive('labelList', function () {
return {
templateUrl: '/static/directives/label-list.html',
restrict: 'C',
replace: true,
scope: {
expand: '@expand',
labels: '=labels'
},
controller: function($scope) {}
};
});

View file

@ -0,0 +1,32 @@
/**
* An element which displays a single label.
*/
angular.module('quay').directive('labelView', function () {
return {
templateUrl: '/static/directives/label-view.html',
restrict: 'C',
replace: true,
scope: {
expand: '@expand',
label: '=label'
},
controller: function($scope, $sanitize) {
$scope.getKind = function(label) {
switch (label.media_type) {
case 'application/json':
return 'json';
}
return '';
};
$scope.viewLabelValue = function() {
bootbox.alert({
size: "small",
title: $scope.label.key,
message: '<pre>' + $sanitize($scope.label.value) + '</pre>'
});
};
}
};
});

View file

@ -0,0 +1,47 @@
/**
* An element which displays the labels on a repository manifest.
*/
angular.module('quay').directive('manifestLabelList', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/manifest-label-list.html',
replace: false,
transclude: true,
restrict: 'C',
scope: {
'repository': '=repository',
'manifestDigest': '=manifestDigest',
},
controller: function($scope, $element, ApiService) {
$scope.labels = null;
var loadLabels = function() {
if (!$scope.repository) {
return;
}
if (!$scope.manifestDigest) {
return;
}
$scope.labels = null;
$scope.loadError = false;
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'manifestref': $scope.manifestDigest
};
ApiService.listManifestLabels(null, params).then(function(resp) {
$scope.labels = resp['labels'];
}, function() {
$scope.loadError = true;
});
};
$scope.$watch('repository', loadLabels);
$scope.$watch('manifestDigest', loadLabels);
}
};
return directiveDefinitionObject;
});