Add UI for viewing labels on a manifest in the tags view
This commit is contained in:
parent
69e476b1f4
commit
f17ef4adda
13 changed files with 336 additions and 112 deletions
|
@ -510,6 +510,7 @@ def populate_database(minimal=False, with_storage=False):
|
||||||
first_label = model.label.create_manifest_label(tag_manifest, 'foo', 'bar', 'manifest')
|
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, 'foo', 'baz', 'api')
|
||||||
model.label.create_manifest_label(tag_manifest, 'anotherlabel', '1234', 'internal')
|
model.label.create_manifest_label(tag_manifest, 'anotherlabel', '1234', 'internal')
|
||||||
|
model.label.create_manifest_label(tag_manifest, 'jsonlabel', '{"hey": "there"}', 'internal')
|
||||||
|
|
||||||
label_metadata = {
|
label_metadata = {
|
||||||
'key': 'foo',
|
'key': 'foo',
|
||||||
|
|
|
@ -127,11 +127,12 @@
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.arepo-panel-tags-element .tag-span {
|
.repo-panel-tags-element tr.expanded-view td {
|
||||||
overflow: hidden;
|
border-bottom: 0px;
|
||||||
text-overflow: ellipsis;
|
}
|
||||||
max-width: 250px;
|
|
||||||
display: inline-block;
|
.repo-panel-tags-element .labels-col {
|
||||||
|
padding-top: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1000px) {
|
@media (max-width: 1000px) {
|
||||||
|
|
9
static/css/directives/ui/label-list.css
Normal file
9
static/css/directives/ui/label-list.css
Normal 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;
|
||||||
|
}
|
25
static/css/directives/ui/label-view.css
Normal file
25
static/css/directives/ui/label-view.css
Normal 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;
|
||||||
|
}
|
8
static/css/directives/ui/manifest-label-list.css
Normal file
8
static/css/directives/ui/manifest-label-list.css
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
.manifest-label-list-element {
|
||||||
|
padding-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manifest-label-list-element .none {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
10
static/directives/label-list.html
Normal file
10
static/directives/label-list.html
Normal 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>
|
8
static/directives/label-view.html
Normal file
8
static/directives/label-view.html
Normal 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>
|
11
static/directives/manifest-label-list.html
Normal file
11
static/directives/manifest-label-list.html
Normal 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>
|
|
@ -1,4 +1,17 @@
|
||||||
<div class="repo-panel-tags-element">
|
<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>
|
<h3 class="tab-header"><span class="hidden-xs">Repository </span>Tags</h3>
|
||||||
<div class="co-alert co-alert-danger" ng-if="hasDefcon1">
|
<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:
|
One or more of your tags has an <strong>extremely critical</strong> vulnerability which should be addressed immediately:
|
||||||
|
@ -103,129 +116,147 @@
|
||||||
<td class="hidden-xs hidden-sm" style="width: 4px"></td>
|
<td class="hidden-xs hidden-sm" style="width: 4px"></td>
|
||||||
</thead>
|
</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-repeat="tag in tags | slice:(tagsPerPage * options.page):(tagsPerPage * (options.page + 1))"
|
||||||
ng-class="checkedTags.isChecked(tag, checkedTags.checked) ? 'checked' : ''"
|
ng-class="checkedTags.isChecked(tag, checkedTags.checked) ? 'checked' : ''"
|
||||||
bindonce>
|
bindonce>
|
||||||
<td><span class="cor-checkable-item" controller="checkedTags" item="tag"></span></td>
|
<tr ng-class="expandedView ? 'expanded-view': ''">
|
||||||
<td class="co-flowing-col"><span class="tag-span"><span bo-text="tag.name"></span></span></td>
|
<td><span class="cor-checkable-item" controller="checkedTags" item="tag"></span></td>
|
||||||
<td class="hidden-xs">
|
<td class="co-flowing-col"><span class="tag-span"><span bo-text="tag.name"></span></span></td>
|
||||||
<span bo-if="tag.last_modified" data-title="{{ tag.last_modified | amDateFormat:'dddd, MMMM Do YYYY, h:mm:ss a' }}" bs-tooltip>
|
<td class="hidden-xs">
|
||||||
<span am-time-ago="tag.last_modified"></span>
|
<span bo-if="tag.last_modified" data-title="{{ tag.last_modified | amDateFormat:'dddd, MMMM Do YYYY, h:mm:ss a' }}" bs-tooltip>
|
||||||
</span>
|
<span am-time-ago="tag.last_modified"></span>
|
||||||
<span bo-if="!tag.last_modified">Unknown</span>
|
|
||||||
</td>
|
|
||||||
<td quay-require="['SECURITY_SCANNER']" class="security-scan-col hidden-xs">
|
|
||||||
<span class="cor-loader-inline" ng-if="getTagVulnerabilities(tag).loading"></span>
|
|
||||||
<span class="vuln-load-error" ng-if="getTagVulnerabilities(tag).hasError"
|
|
||||||
data-title="The vulnerabilities for this tag could not be retrieved at the present time, try again later"
|
|
||||||
bs-tooltip>
|
|
||||||
<i class="fa fa-times-circle"></i>
|
|
||||||
Could not load security information
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span ng-if="!getTagVulnerabilities(tag).loading">
|
|
||||||
<!-- Queued -->
|
|
||||||
<span class="scanning" ng-if="getTagVulnerabilities(tag).status == 'queued'"
|
|
||||||
data-title="The image for this tag is queued to be scanned for vulnerabilities"
|
|
||||||
bs-tooltip>
|
|
||||||
<i class="fa fa-ellipsis-h"></i>
|
|
||||||
Queued
|
|
||||||
</span>
|
</span>
|
||||||
|
<span bo-if="!tag.last_modified">Unknown</span>
|
||||||
<!-- Scan Failed -->
|
</td>
|
||||||
<span class="failed-scan" ng-if="getTagVulnerabilities(tag).status == 'failed'"
|
<td quay-require="['SECURITY_SCANNER']" class="security-scan-col hidden-xs">
|
||||||
data-title="The image for this tag could not be scanned for vulnerabilities"
|
<span class="cor-loader-inline" ng-if="getTagVulnerabilities(tag).loading"></span>
|
||||||
|
<span class="vuln-load-error" ng-if="getTagVulnerabilities(tag).hasError"
|
||||||
|
data-title="The vulnerabilities for this tag could not be retrieved at the present time, try again later"
|
||||||
bs-tooltip>
|
bs-tooltip>
|
||||||
<i class="fa fa-question-circle"></i>
|
|
||||||
Unable to scan
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<!-- No Features -->
|
|
||||||
<span class="failed-scan"
|
|
||||||
ng-if="getTagVulnerabilities(tag).status == 'scanned' && !getTagVulnerabilities(tag).hasFeatures"
|
|
||||||
data-title="The image for this tag has an operating system or package manager unsupported by Quay Security Scanner"
|
|
||||||
bs-tooltip
|
|
||||||
bindonce>
|
|
||||||
<i class="fa fa-times-circle"></i>
|
<i class="fa fa-times-circle"></i>
|
||||||
Unsupported
|
Could not load security information
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- Features and No Vulns -->
|
<span ng-if="!getTagVulnerabilities(tag).loading">
|
||||||
<span class="no-vulns"
|
<!-- Queued -->
|
||||||
ng-if="getTagVulnerabilities(tag).status == 'scanned' && getTagVulnerabilities(tag).hasFeatures && !getTagVulnerabilities(tag).hasVulnerabilities"
|
<span class="scanning" ng-if="getTagVulnerabilities(tag).status == 'queued'"
|
||||||
data-title="The image for this tag has no vulnerabilities as found in our database"
|
data-title="The image for this tag is queued to be scanned for vulnerabilities"
|
||||||
bs-tooltip
|
bs-tooltip>
|
||||||
bindonce>
|
<i class="fa fa-ellipsis-h"></i>
|
||||||
<a bo-href-i="/repository/{{ repository.namespace }}/{{ repository.name }}/image/{{ tag.image_id }}?tab=vulnerabilities">
|
Queued
|
||||||
<i class="fa fa-check-circle"></i>
|
</span>
|
||||||
Passed
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<!-- Vulns -->
|
<!-- Scan Failed -->
|
||||||
<span ng-if="getTagVulnerabilities(tag).status == 'scanned' && getTagVulnerabilities(tag).hasFeatures && getTagVulnerabilities(tag).hasVulnerabilities"
|
<span class="failed-scan" ng-if="getTagVulnerabilities(tag).status == 'failed'"
|
||||||
ng-class="getTagVulnerabilities(tag).highestVulnerability.Priority"
|
data-title="The image for this tag could not be scanned for vulnerabilities"
|
||||||
class="has-vulns" bindonce>
|
bs-tooltip>
|
||||||
|
<i class="fa fa-question-circle"></i>
|
||||||
|
Unable to scan
|
||||||
|
</span>
|
||||||
|
|
||||||
<a class="vuln-link" bo-href-i="/repository/{{ repository.namespace }}/{{ repository.name }}/image/{{ tag.image_id }}?tab=vulnerabilities"
|
<!-- No Features -->
|
||||||
data-title="This tag has {{ getTagVulnerabilities(tag).vulnerabilities.length }} vulnerabilities across {{ getTagVulnerabilities(tag).featuresInfo.brokenFeaturesCount }} packages"
|
<span class="failed-scan"
|
||||||
bs-tooltip>
|
ng-if="getTagVulnerabilities(tag).status == 'scanned' && !getTagVulnerabilities(tag).hasFeatures"
|
||||||
<!-- Donut -->
|
data-title="The image for this tag has an operating system or package manager unsupported by Quay Security Scanner"
|
||||||
<span class="donut-chart" width="24" data="getTagVulnerabilities(tag).featuresInfo.severityBreakdown"></span>
|
bs-tooltip
|
||||||
|
bindonce>
|
||||||
|
<i class="fa fa-times-circle"></i>
|
||||||
|
Unsupported
|
||||||
|
</span>
|
||||||
|
|
||||||
<!-- Messaging -->
|
<!-- Features and No Vulns -->
|
||||||
<span ng-if="getTagVulnerabilities(tag).featuresInfo.fixableFeatureCount == 0">
|
<span class="no-vulns"
|
||||||
{{ getTagVulnerabilities(tag).featuresInfo.brokenFeaturesCount }} vulnerable package<span ng-if="getTagVulnerabilities(tag).featuresInfo.brokenFeaturesCount != 1">s</span>
|
ng-if="getTagVulnerabilities(tag).status == 'scanned' && getTagVulnerabilities(tag).hasFeatures && !getTagVulnerabilities(tag).hasVulnerabilities"
|
||||||
</span>
|
data-title="The image for this tag has no vulnerabilities as found in our database"
|
||||||
<span ng-if="getTagVulnerabilities(tag).featuresInfo.fixableFeatureCount > 0">
|
bs-tooltip
|
||||||
{{ getTagVulnerabilities(tag).featuresInfo.fixableFeatureCount }} fixable package<span ng-if="getTagVulnerabilities(tag).featuresInfo.fixableFeatureCount != 1">s</span>
|
bindonce>
|
||||||
</span>
|
<a bo-href-i="/repository/{{ repository.namespace }}/{{ repository.name }}/image/{{ tag.image_id }}?tab=vulnerabilities">
|
||||||
</a>
|
<i class="fa fa-check-circle"></i>
|
||||||
</span>
|
Passed
|
||||||
</span>
|
</a>
|
||||||
</td>
|
</span>
|
||||||
<td class="hidden-sm hidden-xs" bo-text="tag.size | bytes"></td>
|
|
||||||
<td class="hidden-xs hidden-sm image-id-col">
|
<!-- Vulns -->
|
||||||
<span class="image-link" repository="repository" image-id="tag.image_id" manifest-digest="tag.manifest_digest"></span>
|
<span ng-if="getTagVulnerabilities(tag).status == 'scanned' && getTagVulnerabilities(tag).hasFeatures && getTagVulnerabilities(tag).hasVulnerabilities"
|
||||||
</td>
|
ng-class="getTagVulnerabilities(tag).highestVulnerability.Priority"
|
||||||
<td class="hidden-xs hidden-sm image-track"
|
class="has-vulns" bindonce>
|
||||||
ng-if="imageTracks.length > maxTrackCount" bindonce>
|
|
||||||
<span ng-repeat="it in imageTracks">
|
<a class="vuln-link" bo-href-i="/repository/{{ repository.namespace }}/{{ repository.name }}/image/{{ tag.image_id }}?tab=vulnerabilities"
|
||||||
<span ng-repeat="entry in it.entries">
|
data-title="This tag has {{ getTagVulnerabilities(tag).vulnerabilities.length }} vulnerabilities across {{ getTagVulnerabilities(tag).featuresInfo.brokenFeaturesCount }} packages"
|
||||||
<span class="image-track-dot" bo-if="entry.image_id == tag.image_id"
|
bs-tooltip>
|
||||||
bo-style="{'borderColor': entry.color}" ng-click="selectTrack(entry)"></span>
|
<!-- Donut -->
|
||||||
|
<span class="donut-chart" width="24" data="getTagVulnerabilities(tag).featuresInfo.severityBreakdown"></span>
|
||||||
|
|
||||||
|
<!-- Messaging -->
|
||||||
|
<span ng-if="getTagVulnerabilities(tag).featuresInfo.fixableFeatureCount == 0">
|
||||||
|
{{ getTagVulnerabilities(tag).featuresInfo.brokenFeaturesCount }} vulnerable package<span ng-if="getTagVulnerabilities(tag).featuresInfo.brokenFeaturesCount != 1">s</span>
|
||||||
|
</span>
|
||||||
|
<span ng-if="getTagVulnerabilities(tag).featuresInfo.fixableFeatureCount > 0">
|
||||||
|
{{ getTagVulnerabilities(tag).featuresInfo.fixableFeatureCount }} fixable package<span ng-if="getTagVulnerabilities(tag).featuresInfo.fixableFeatureCount != 1">s</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="hidden-xs hidden-sm image-track" ng-repeat="it in imageTracks"
|
<td class="hidden-sm hidden-xs" bo-text="tag.size | bytes"></td>
|
||||||
ng-if="imageTracks.length <= maxTrackCount" bindonce>
|
<td class="hidden-xs hidden-sm image-id-col">
|
||||||
<span ng-repeat="entry in it.entries">
|
<span class="image-link" repository="repository" image-id="tag.image_id" manifest-digest="tag.manifest_digest"></span>
|
||||||
<span class="image-track-dot" bo-if="entry.image_id == tag.image_id"
|
</td>
|
||||||
bo-style="{'borderColor': entry.color}" ng-click="selectTrack(entry)"></span>
|
<td class="hidden-xs hidden-sm image-track"
|
||||||
<span class="image-track-line" bo-class="trackLineClass($parent.$parent.$parent.$index, entry)"
|
ng-if="imageTracks.length > maxTrackCount" bindonce>
|
||||||
bo-style="{'borderColor': entry.color}"></span>
|
<span ng-repeat="it in imageTracks">
|
||||||
</span>
|
<span ng-repeat="entry in it.entries">
|
||||||
</td>
|
<span class="image-track-dot" bo-if="entry.image_id == tag.image_id"
|
||||||
<td class="options-col">
|
bo-style="{'borderColor': entry.color}" ng-click="selectTrack(entry)"></span>
|
||||||
<i class="fa fa-download" data-title="Fetch Tag" bs-tooltip
|
</span>
|
||||||
ng-click="fetchTagActionHandler.askFetchTag(tag)">
|
|
||||||
</i>
|
|
||||||
</td>
|
|
||||||
<td class="options-col">
|
|
||||||
<span bo-if="repository.can_write">
|
|
||||||
<span class="cor-options-menu">
|
|
||||||
<span class="cor-option" option-click="askAddTag(tag)">
|
|
||||||
<i class="fa fa-plus"></i> Add New Tag
|
|
||||||
</span>
|
</span>
|
||||||
<span class="cor-option" option-click="askDeleteTag(tag.name)">
|
</td>
|
||||||
<i class="fa fa-times"></i> Delete Tag
|
<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-dot" bo-if="entry.image_id == tag.image_id"
|
||||||
|
bo-style="{'borderColor': entry.color}" ng-click="selectTrack(entry)"></span>
|
||||||
|
<span class="image-track-line" bo-class="trackLineClass($parent.$parent.$parent.$index, entry)"
|
||||||
|
bo-style="{'borderColor': entry.color}"></span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="options-col">
|
||||||
|
<i class="fa fa-download" data-title="Fetch Tag" bs-tooltip
|
||||||
|
ng-click="fetchTagActionHandler.askFetchTag(tag)">
|
||||||
|
</i>
|
||||||
|
</td>
|
||||||
|
<td class="options-col">
|
||||||
|
<span bo-if="repository.can_write">
|
||||||
|
<span class="cor-options-menu">
|
||||||
|
<span class="cor-option" option-click="askAddTag(tag)">
|
||||||
|
<i class="fa fa-plus"></i> Add New Tag
|
||||||
|
</span>
|
||||||
|
<span class="cor-option" option-click="askDeleteTag(tag.name)">
|
||||||
|
<i class="fa fa-times"></i> Delete Tag
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</td>
|
||||||
</td>
|
<td class="options-col hidden-xs hidden-sm"><!-- Whitespace col --></td>
|
||||||
<td class="options-col hidden-xs hidden-sm"><!-- Whitespace col --></td>
|
</tr>
|
||||||
</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>
|
</table>
|
||||||
|
|
||||||
<div class="empty" ng-if="allTags.length && !tags.length">
|
<div class="empty" ng-if="allTags.length && !tags.length">
|
||||||
|
|
|
@ -35,6 +35,8 @@ angular.module('quay').directive('repoPanelTags', function () {
|
||||||
$scope.tagActionHandler = null;
|
$scope.tagActionHandler = null;
|
||||||
$scope.tagsPerPage = 25;
|
$scope.tagsPerPage = 25;
|
||||||
|
|
||||||
|
$scope.expandedView = false;
|
||||||
|
|
||||||
$scope.imageVulnerabilities = {};
|
$scope.imageVulnerabilities = {};
|
||||||
$scope.defcon1 = {};
|
$scope.defcon1 = {};
|
||||||
$scope.hasDefcon1 = false;
|
$scope.hasDefcon1 = false;
|
||||||
|
@ -262,6 +264,26 @@ angular.module('quay').directive('repoPanelTags', function () {
|
||||||
$scope.checkedTags.setChecked($scope.tags);
|
$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) {
|
$scope.trackLineClass = function(index, track_info) {
|
||||||
var startIndex = $.inArray(track_info.tags[0], $scope.tags);
|
var startIndex = $.inArray(track_info.tags[0], $scope.tags);
|
||||||
var endIndex = $.inArray(track_info.tags[track_info.tags.length - 1], $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.setTab('history');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.setExpanded = function(expanded) {
|
||||||
|
$scope.expandedView = expanded;
|
||||||
|
};
|
||||||
|
|
||||||
$scope.getTagNames = function(checked) {
|
$scope.getTagNames = function(checked) {
|
||||||
var names = checked.map(function(tag) {
|
var names = checked.map(function(tag) {
|
||||||
return tag.name;
|
return tag.name;
|
||||||
|
|
15
static/js/directives/ui/label-list.js
Normal file
15
static/js/directives/ui/label-list.js
Normal 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) {}
|
||||||
|
};
|
||||||
|
});
|
32
static/js/directives/ui/label-view.js
Normal file
32
static/js/directives/ui/label-view.js
Normal 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>'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
47
static/js/directives/ui/manifest-label-list.js
Normal file
47
static/js/directives/ui/manifest-label-list.js
Normal 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;
|
||||||
|
});
|
Reference in a new issue