diff --git a/src/ui_ng/lib/src/create-edit-label/create-edit-label.component.css.ts b/src/ui_ng/lib/src/create-edit-label/create-edit-label.component.css.ts index 8bf46d759..fbb1d6448 100644 --- a/src/ui_ng/lib/src/create-edit-label/create-edit-label.component.css.ts +++ b/src/ui_ng/lib/src/create-edit-label/create-edit-label.component.css.ts @@ -20,4 +20,5 @@ export const CREATE_EDIT_LABEL_STYLE: string = ` height:22px; min-width: 26px;} .dropdown-item{border:0px; color: white; font-size:12px;} + .dropdown .dropdown-toggle.btn{padding:0 !important;} `; \ No newline at end of file diff --git a/src/ui_ng/lib/src/create-edit-rule/create-edit-rule.component.css.ts b/src/ui_ng/lib/src/create-edit-rule/create-edit-rule.component.css.ts index aa63742f4..b842ce255 100644 --- a/src/ui_ng/lib/src/create-edit-rule/create-edit-rule.component.css.ts +++ b/src/ui_ng/lib/src/create-edit-rule/create-edit-rule.component.css.ts @@ -19,7 +19,7 @@ h4{ } .colorRed{color: red;} .colorRed a{text-decoration: underline;color: #007CBB;} -.alertLabel{display:block; margin-top:0; line-height:1em; font-size:12px;} +.alertLabel{display:block; margin-top:2px; line-height:1em; font-size:12px;} .inputWidth{width: 270px;} .endpointSelect{ width: 270px; margin-right: 20px;} diff --git a/src/ui_ng/lib/src/label-piece/label-piece.template.ts b/src/ui_ng/lib/src/label-piece/label-piece.template.ts index cc8d94336..9fd26a292 100644 --- a/src/ui_ng/lib/src/label-piece/label-piece.template.ts +++ b/src/ui_ng/lib/src/label-piece/label-piece.template.ts @@ -11,7 +11,7 @@ export const LABEL_PIEICE_TEMPLATE: string = ` `; export const LABEL_PIEICE_STYLES: string = ` - .label{border: none; color:#222;padding-top:2px;} + .label{border: none; color:#222;padding-top:2px; max-width:140px;} .label clr-icon{ margin-right: 3px; display:block;} .btn-group .dropdown-menu clr-icon{display:block;} `; \ No newline at end of file diff --git a/src/ui_ng/lib/src/label/label.component.ts b/src/ui_ng/lib/src/label/label.component.ts index 0530f793f..6cb1727c3 100644 --- a/src/ui_ng/lib/src/label/label.component.ts +++ b/src/ui_ng/lib/src/label/label.component.ts @@ -112,8 +112,8 @@ export class LabelComponent implements OnInit { this.batchDelectionInfos.push(initBatchMessage); }); let deletionMessage = new ConfirmationMessage( - 'REPLICATION.DELETION_TITLE_TARGET', - 'REPLICATION.DELETION_SUMMARY_TARGET', + 'LABEL.DELETION_TITLE_TARGET', + 'LABEL.DELETION_SUMMARY_TARGET', targetNames.join(', ') || '', targets, ConfirmationTargets.TARGET, diff --git a/src/ui_ng/lib/src/repository-listview/repository-listview.component.ts b/src/ui_ng/lib/src/repository-listview/repository-listview.component.ts index 4886db198..6440c9f05 100644 --- a/src/ui_ng/lib/src/repository-listview/repository-listview.component.ts +++ b/src/ui_ng/lib/src/repository-listview/repository-listview.component.ts @@ -176,6 +176,13 @@ export class RepositoryListviewComponent implements OnChanges, OnInit { }); return; } + if (error.status === 503) { + Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'), + this.translateService.get('REPOSITORY.TAGS_NO_DELETE')).subscribe(res => { + findedList = BathInfoChanges(findedList, res[0], false, true, res[1]); + }); + return; + } this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => { findedList = BathInfoChanges(findedList, res, false, true); }); diff --git a/src/ui_ng/lib/src/tag/tag-detail.component.html.ts b/src/ui_ng/lib/src/tag/tag-detail.component.html.ts index af9394f5e..e8989d94a 100644 --- a/src/ui_ng/lib/src/tag/tag-detail.component.html.ts +++ b/src/ui_ng/lib/src/tag/tag-detail.component.html.ts @@ -30,7 +30,7 @@ export const TAG_DETAIL_HTML: string = ` -
+
diff --git a/src/ui_ng/lib/src/tag/tag-detail.component.spec.ts b/src/ui_ng/lib/src/tag/tag-detail.component.spec.ts index 20da427dd..30e7b053f 100644 --- a/src/ui_ng/lib/src/tag/tag-detail.component.spec.ts +++ b/src/ui_ng/lib/src/tag/tag-detail.component.spec.ts @@ -148,18 +148,4 @@ describe('TagDetailComponent (inline template)', () => { }); })); - it('should display vulnerability details', async(() => { - fixture.detectChanges(); - - fixture.whenStable().then(() => { - fixture.detectChanges(); - - let el: HTMLElement = fixture.nativeElement.querySelector('.second-column'); - expect(el).toBeTruthy(); - let el2: HTMLElement = el.querySelector('div'); - expect(el2).toBeTruthy(); - expect(el2.textContent.trim()).toEqual("13 VULNERABILITY.SEVERITY.HIGHTAG.LEVEL_VULNERABILITIES"); - }); - })); - }); \ No newline at end of file diff --git a/src/ui_ng/lib/src/tag/tag-detail.component.ts b/src/ui_ng/lib/src/tag/tag-detail.component.ts index 9a1be6b3a..5de443831 100644 --- a/src/ui_ng/lib/src/tag/tag-detail.component.ts +++ b/src/ui_ng/lib/src/tag/tag-detail.component.ts @@ -25,6 +25,7 @@ export class TagDetailComponent implements OnInit { @Input() tagId: string; @Input() repositoryId: string; @Input() withAdmiral: boolean; + @Input() withClair: boolean; tagDetails: Tag = { name: "--", size: "--", diff --git a/src/ui_ng/lib/src/tag/tag.component.css.ts b/src/ui_ng/lib/src/tag/tag.component.css.ts index 36f5e96e6..c7c9b3420 100644 --- a/src/ui_ng/lib/src/tag/tag.component.css.ts +++ b/src/ui_ng/lib/src/tag/tag.component.css.ts @@ -67,10 +67,6 @@ export const TAG_STYLE = ` :host >>> .signpost-content-header{display:none;} .filterLabelPiece{position: absolute; bottom :0px;z-index:1;} .dropdown .dropdown-toggle.btn { - padding-right: 1rem; - border-left-width: 0; - border-right-width: 0; - border-radius: 0; - margin-top: -2px; + margin: .25rem .5rem .25rem 0; } `; \ No newline at end of file diff --git a/src/ui_ng/lib/src/tag/tag.component.html.ts b/src/ui_ng/lib/src/tag/tag.component.html.ts index 36d3e5fac..8cd4270c5 100644 --- a/src/ui_ng/lib/src/tag/tag.component.html.ts +++ b/src/ui_ng/lib/src/tag/tag.component.html.ts @@ -44,23 +44,23 @@ export const TAG_TEMPLATE = ` - - - -
- -
-
{{'LABEL.NO_LABELS' | translate }}
-
- -
-
-
-
+ + + +
+ +
+
{{'LABEL.NO_LABELS' | translate }}
+
+ +
+
+
+
{{'REPOSITORY.TAG' | translate}} @@ -74,9 +74,8 @@ export const TAG_TEMPLATE = ` {{'REPOSITORY.LABELS' | translate}} {{'TAG.PLACEHOLDER' | translate }} - - {{t.name}} - {{t.name}} + + {{t.name}} {{sizeTransform(t.size)}} diff --git a/src/ui_ng/lib/src/tag/tag.component.ts b/src/ui_ng/lib/src/tag/tag.component.ts index ee0137069..d820a2022 100644 --- a/src/ui_ng/lib/src/tag/tag.component.ts +++ b/src/ui_ng/lib/src/tag/tag.component.ts @@ -217,7 +217,13 @@ export class TagComponent implements OnInit, AfterViewInit { st.page.size = this.pageSize; st.page.from = 0; st.page.to = this.pageSize - 1; - st.filters = [{property: "name", value: this.lastFilteredTagName}]; + let selectedLab = this.imageFilterLabels.find(label => label.iconsShow === true); + if (selectedLab) { + st.filters = [{property: 'name', value: this.lastFilteredTagName}, {property: 'labels.name', value: selectedLab.label}]; + }else { + st.filters = [{property: 'name', value: this.lastFilteredTagName}]; + } + this.clrLoad(st); } @@ -514,6 +520,13 @@ export class TagComponent implements OnInit, AfterViewInit { findedList = BathInfoChanges(findedList, res); }); }).catch(error => { + if (error.status === 503) { + Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'), + this.translateService.get('REPOSITORY.TAGS_NO_DELETE')).subscribe(res => { + findedList = BathInfoChanges(findedList, res[0], false, true, res[1]); + }); + return; + } this.translateService.get("BATCH.DELETED_FAILURE").subscribe(res => { findedList = BathInfoChanges(findedList, res, false, true); }); diff --git a/src/ui_ng/package.json b/src/ui_ng/package.json index 7940d6aa6..7df6aac21 100644 --- a/src/ui_ng/package.json +++ b/src/ui_ng/package.json @@ -31,7 +31,7 @@ "clarity-icons": "^0.10.17", "clarity-ui": "^0.10.27", "core-js": "^2.4.1", - "harbor-ui": "0.6.61", + "harbor-ui": "0.6.62", "intl": "^1.2.5", "mutationobserver-shim": "^0.3.2", "ngx-cookie": "^1.0.0", diff --git a/src/ui_ng/src/app/app-config.ts b/src/ui_ng/src/app/app-config.ts index ab63b2dfc..6398e8570 100644 --- a/src/ui_ng/src/app/app-config.ts +++ b/src/ui_ng/src/app/app-config.ts @@ -27,6 +27,7 @@ export class AppConfig { clair_vulnerability_status?: ClairDBStatus; next_scan_all: number; registry_storage_provider_name: string; + read_only: boolean; constructor() { // Set default value @@ -46,5 +47,6 @@ export class AppConfig { }; this.next_scan_all = 0; this.registry_storage_provider_name = ""; + this.read_only = false; } } \ No newline at end of file diff --git a/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.css b/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.css index d39e2622d..dc8ffec7b 100644 --- a/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.css +++ b/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.css @@ -5,6 +5,9 @@ .container-override { position: relative !important; } +.content-container{ + position: relative; +} .start-content-padding { padding: 0px !important; diff --git a/src/ui_ng/src/app/base/navigator/navigator.component.ts b/src/ui_ng/src/app/base/navigator/navigator.component.ts index d0ee3f94c..6a99c7006 100644 --- a/src/ui_ng/src/app/base/navigator/navigator.component.ts +++ b/src/ui_ng/src/app/base/navigator/navigator.component.ts @@ -69,12 +69,16 @@ export class NavigatorComponent implements OnInit { this.translate.onLangChange.subscribe((langChange: {lang: string}) => { this.selectedLang = langChange.lang; //Keep in cookie for next use - let opt:CookieOptions = {path: '/', expires: new Date(Date.now() + 3600*1000*24*31)}; + let opt: CookieOptions = {path: '/', expires: new Date(Date.now() + 3600*1000*24*31)}; this.cookie.put("harbor-lang", langChange.lang, opt); }); if (this.appConfigService.isIntegrationMode()) { this.appTitle = 'APP_TITLE.VIC'; } + + if (this.appConfigService.getConfig().read_only) { + this.msgHandler.handleReadOnly(); + } } public get isSessionValid(): boolean { diff --git a/src/ui_ng/src/app/config/config.component.html b/src/ui_ng/src/app/config/config.component.html index 6f00dce28..5e37f2ffc 100644 --- a/src/ui_ng/src/app/config/config.component.html +++ b/src/ui_ng/src/app/config/config.component.html @@ -18,6 +18,9 @@ +
@@ -34,6 +37,9 @@
+
+ +
diff --git a/src/ui_ng/src/app/config/config.component.ts b/src/ui_ng/src/app/config/config.component.ts index cadd50437..6c4091831 100644 --- a/src/ui_ng/src/app/config/config.component.ts +++ b/src/ui_ng/src/app/config/config.component.ts @@ -31,6 +31,7 @@ import { SystemSettingsComponent, VulnerabilityConfigComponent, } from 'harbor-ui'; +import {RepoReadOnlyComponent} from "./repo/repo-read-only.component"; const fakePass = 'aWpLOSYkIzJTTU4wMDkx'; const TabLinkContentMap = { @@ -39,7 +40,8 @@ const TabLinkContentMap = { 'config-email': 'email', 'config-system': 'system_settings', 'config-vulnerability': 'vulnerability', - 'config-label': 'system_label' + 'config-label': 'system_label', + 'config-repo': 'repoReadOnly' }; @Component({ @@ -60,6 +62,7 @@ export class ConfigurationComponent implements OnInit, OnDestroy { @ViewChild(VulnerabilityConfigComponent) vulnerabilityConfig: VulnerabilityConfigComponent; @ViewChild(ConfigurationEmailComponent) mailConfig: ConfigurationEmailComponent; @ViewChild(ConfigurationAuthComponent) authConfig: ConfigurationAuthComponent; + @ViewChild(RepoReadOnlyComponent) repoConfig: RepoReadOnlyComponent; constructor( private msgHandler: MessageHandlerService, @@ -125,6 +128,9 @@ export class ConfigurationComponent implements OnInit, OnDestroy { case 'config-vulnerability': properties = ['scan_all_policy']; break; + case 'config-repo': + properties = ['read_only']; + break; default: return null; } @@ -262,6 +268,14 @@ export class ConfigurationComponent implements OnInit, OnDestroy { // HERE we choose force way this.retrieveConfig(); + if (changes['read_only']) { + this.msgHandler.handleReadOnly(); + } + + if (changes['read_only'].toString() === "false") { + this.msgHandler.clear(); + } + // Reload bootstrap option this.appConfigService.load().catch(error => console.error('Failed to reload bootstrap option with error: ', error)); @@ -273,7 +287,7 @@ export class ConfigurationComponent implements OnInit, OnDestroy { }); } else { // Inprop situation, should not come here - console.error('Save obort becasue nothing changed'); + console.error('Save abort because nothing changed'); } } diff --git a/src/ui_ng/src/app/config/config.module.ts b/src/ui_ng/src/app/config/config.module.ts index 8e16832f6..4c955bf40 100644 --- a/src/ui_ng/src/app/config/config.module.ts +++ b/src/ui_ng/src/app/config/config.module.ts @@ -19,6 +19,7 @@ import { ConfigurationComponent } from './config.component'; import { ConfigurationService } from './config.service'; import { ConfigurationAuthComponent } from './auth/config-auth.component'; import { ConfigurationEmailComponent } from './email/config-email.component'; +import {RepoReadOnlyComponent} from "./repo/repo-read-only.component"; @NgModule({ imports: [ @@ -28,7 +29,9 @@ import { ConfigurationEmailComponent } from './email/config-email.component'; declarations: [ ConfigurationComponent, ConfigurationAuthComponent, - ConfigurationEmailComponent], + ConfigurationEmailComponent, + RepoReadOnlyComponent + ], exports: [ConfigurationComponent], providers: [ConfigurationService] }) diff --git a/src/ui_ng/src/app/config/repo/repo-read-only.component.ts b/src/ui_ng/src/app/config/repo/repo-read-only.component.ts new file mode 100644 index 000000000..e94aac632 --- /dev/null +++ b/src/ui_ng/src/app/config/repo/repo-read-only.component.ts @@ -0,0 +1,25 @@ + +import {Component, Input, ViewChild} from "@angular/core"; +import {Configuration} from "harbor-ui"; +import {NgForm} from "@angular/forms"; + +@Component({ + selector: 'repo-read-only', + templateUrl: 'repo-read-only.html', +}) +export class RepoReadOnlyComponent { + + @Input('repoConfig') currentConfig: Configuration = new Configuration(); + + @ViewChild('repoConfigFrom') repoForm: NgForm; + + constructor() { } + + disabled(prop: any) { + return !(prop && prop.editable); + } + + setInsecureReadOnlyValue($event: any) { + this.currentConfig.read_only.value = $event; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/config/repo/repo-read-only.html b/src/ui_ng/src/app/config/repo/repo-read-only.html new file mode 100644 index 000000000..5a5c8d285 --- /dev/null +++ b/src/ui_ng/src/app/config/repo/repo-read-only.html @@ -0,0 +1,13 @@ +
+
+
+ + + + + {{'CONFIG.REPO_TOOLTIP' | translate}} + + +
+
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/global-message/message.component.css b/src/ui_ng/src/app/global-message/message.component.css index 4f4e7bbe8..3d3a4d4e1 100644 --- a/src/ui_ng/src/app/global-message/message.component.css +++ b/src/ui_ng/src/app/global-message/message.component.css @@ -1,7 +1,7 @@ .global-message-alert { - position: fixed; - top: 60px; - left: 0px; + position: absolute; + top: 0; + left: 0; width: 100%; z-index: 999; } @@ -11,4 +11,5 @@ } :host >>> .alert-icon-wrapper{ display: inline; -} \ No newline at end of file +} +:host >>> .alert.alert-app-level.alert-warning{padding: 0;} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/tag-detail/tag-detail-page.component.html b/src/ui_ng/src/app/repository/tag-detail/tag-detail-page.component.html index 08e9f7776..2a0a6b995 100644 --- a/src/ui_ng/src/app/repository/tag-detail/tag-detail-page.component.html +++ b/src/ui_ng/src/app/repository/tag-detail/tag-detail-page.component.html @@ -1,3 +1,3 @@
- +
\ No newline at end of file diff --git a/src/ui_ng/src/app/repository/tag-detail/tag-detail-page.component.ts b/src/ui_ng/src/app/repository/tag-detail/tag-detail-page.component.ts index 4c96bc373..762c62e3b 100644 --- a/src/ui_ng/src/app/repository/tag-detail/tag-detail-page.component.ts +++ b/src/ui_ng/src/app/repository/tag-detail/tag-detail-page.component.ts @@ -41,6 +41,10 @@ export class TagDetailPageComponent implements OnInit { return this.appConfigService.getConfig().with_admiral; } + get withClair(): boolean { + return this.appConfigService.getConfig().with_clair; + } + goBack(tag: string): void { this.router.navigate(["harbor", "projects", this.projectId, "repositories", tag]); } diff --git a/src/ui_ng/src/app/shared/message-handler/message-handler.service.ts b/src/ui_ng/src/app/shared/message-handler/message-handler.service.ts index 010e9011a..45af7639a 100644 --- a/src/ui_ng/src/app/shared/message-handler/message-handler.service.ts +++ b/src/ui_ng/src/app/shared/message-handler/message-handler.service.ts @@ -53,6 +53,10 @@ export class MessageHandlerService implements ErrorHandler{ } } + public handleReadOnly(): void { + this.msgService.announceAppLevelMessage(503, 'REPO_READ_ONLY', AlertType.WARNING); + } + public showError(message: string, params: any): void { if (!params) { params = {}; diff --git a/src/ui_ng/src/app/shared/route/auth-user-activate.service.ts b/src/ui_ng/src/app/shared/route/auth-user-activate.service.ts index 729424ad8..e6c831235 100644 --- a/src/ui_ng/src/app/shared/route/auth-user-activate.service.ts +++ b/src/ui_ng/src/app/shared/route/auth-user-activate.service.ts @@ -48,6 +48,10 @@ export class AuthCheckGuard implements CanActivate, CanActivateChild { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { //When routing change, clear this.msgHandler.clear(); + if (this.appConfigService.getConfig().read_only.toString() === 'true') { + this.msgHandler.handleReadOnly(); + } + this.searchTrigger.closeSearch(true); return new Promise((resolve, reject) => { //Before activating, we firstly need to confirm whether the route is coming from peer part - admiral diff --git a/src/ui_ng/src/i18n/lang/en-us-lang.json b/src/ui_ng/src/i18n/lang/en-us-lang.json index 525ed7bd8..1c63ad8b8 100644 --- a/src/ui_ng/src/i18n/lang/en-us-lang.json +++ b/src/ui_ng/src/i18n/lang/en-us-lang.json @@ -178,7 +178,7 @@ "REPLICATION": "Replication", "USERS": "Members", "LOGS": "Logs", - "LABELS": "labels", + "LABELS": "Labels", "PROJECTS": "Projects", "CONFIG": "Configuration" }, @@ -390,6 +390,7 @@ "DELETION_SUMMARY_TAG": "Do you want to delete tag {{param}}?", "DELETION_TITLE_TAG_DENIED": "Signed tag cannot be deleted", "DELETION_SUMMARY_TAG_DENIED": "The tag must be removed from the Notary before it can be deleted.\nDelete from Notary via this command:\n{{param}}", + "TAGS_NO_DELETE": "Delete is prohibited in read only mode.", "FILTER_FOR_REPOSITORIES": "Filter Repositories", "TAG": "Tag", "SIZE": "Size", @@ -414,7 +415,7 @@ "INFO": "Info", "NO_INFO": "No description info for this repository", "IMAGE": "Images", - "LABELS": ":labels", + "LABELS": "Labels", "ADD_TO_IMAGE": "Add labels to this image", "FILTER_BY_LABEL": "Filter projects by label", "ADD_LABELS": "Add labels", @@ -442,7 +443,7 @@ "AUTH": "Authentication", "REPLICATION": "Replication", "EMAIL": "Email", - "LABEL": "Label", + "LABEL": "Labels", "REPOSITORY": "Repository", "REPO_READ_ONLY": "Repository Read Only", "SYSTEM": "System Settings", @@ -487,7 +488,8 @@ "PRO_CREATION_RESTRICTION": "The flag to define what users have permission to create projects. By default, everyone can create a project. Set to 'Admin Only' so that only an administrator can create a project.", "ROOT_CERT_DOWNLOAD": "Download the root certificate of registry.", "SCANNING_POLICY": "Set image scanning policy based on different requirements. 'None': No active policy; 'Daily At': Triggering scanning at the specified time everyday.", - "VERIFY_CERT": "Verify Cert from LDAP Server" + "VERIFY_CERT": "Verify Cert from LDAP Server", + "REPO_TOOLTIP": "If true, means maintenance in Progress. During this period, you cannot delete repository, tag and push image." }, "LDAP": { "URL": "LDAP URL", @@ -620,6 +622,7 @@ "OS": "OS", "SCAN_COMPLETION_TIME": "Scan Completed", "IMAGE_VULNERABILITIES": "Image Vulnerabilities", + "LEVEL_VULNERABILITIES": "Level Vulnerabilities", "PLACEHOLDER": "We couldn't find any tags!", "COPY_ERROR": "Copy failed, please try to manually copy.", "FILTER_FOR_TAGS": "Filter Tags" @@ -634,7 +637,9 @@ "LABEL_NAME": "Label Name", "COLOR": "Color", "FILTER_LABEL_PLACEHOLDER": "Filter Labels", - "NO_LABELS": "No labels" + "NO_LABELS": "No labels", + "DELETION_TITLE_TARGET": "Confirm Label Deletion", + "DELETION_SUMMARY_TARGET": "Do you want to delete {{param}}?" }, "WEEKLY": { "MONDAY": "Monday", @@ -647,6 +652,7 @@ }, "UNKNOWN_ERROR": "Unknown errors have occurred. Please try again later.", "UNAUTHORIZED_ERROR": "Your session is invalid or has expired. You need to sign in to continue your action.", + "REPO_READ_ONLY": "Maintenance in Progress: During this period, you cannot delete repository, tag and push image.", "FORBIDDEN_ERROR": "You do not have the proper privileges to perform the action.", "GENERAL_ERROR": "Errors have occurred when performing service call: {{param}}.", "BAD_REQUEST_ERROR": "We are unable to perform your action because of a bad request.", diff --git a/src/ui_ng/src/i18n/lang/es-es-lang.json b/src/ui_ng/src/i18n/lang/es-es-lang.json index 9f3f9d00c..b8097859b 100644 --- a/src/ui_ng/src/i18n/lang/es-es-lang.json +++ b/src/ui_ng/src/i18n/lang/es-es-lang.json @@ -178,7 +178,7 @@ "REPLICATION": "Replicación", "USERS": "Miembros", "LOGS": "Logs", - "LABELS": "labels", + "LABELS": "Labels", "PROJECTS": "Proyectos", "CONFIG": "Configuración" }, @@ -390,6 +390,7 @@ "DELETION_SUMMARY_TAG": "¿Quiere eliminar la etiqueta {{param}}?", "DELETION_TITLE_TAG_DENIED": "La etiqueta firmada no puede ser eliminada", "DELETION_SUMMARY_TAG_DENIED": "La etiqueta debe ser eliminada de la Notaría antes de eliminarla.\nEliminarla de la Notaría con este comando:\n{{param}}", + "TAGS_NO_DELETE": "Delete is prohibited in read only mode.", "FILTER_FOR_REPOSITORIES": "Filtrar Repositorios", "TAG": "Etiqueta", "SIZE": "Size", @@ -442,7 +443,7 @@ "AUTH": "Autentificación", "REPLICATION": "Replicación", "EMAIL": "Email", - "LABEL": "Label", + "LABEL": "Labels", "REPOSITORY": "Repository", "REPO_READ_ONLY": "Repository Read Only", "SYSTEM": "Opciones del Sistema", @@ -487,7 +488,8 @@ "PRO_CREATION_RESTRICTION": "Marca para definir qué usuarios tienen permisos para crear proyectos. Por defecto, todos pueden crear proyectos. Seleccione 'Solo Administradores' para que solamente los administradores puedan crear proyectos.", "ROOT_CERT_DOWNLOAD": "Download the root certificate of registry.", "SCANNING_POLICY": "Set image scanning policy based on different requirements. 'None': No active policy; 'Daily At': Triggering scanning at the specified time everyday.", - "VERIFY_CERT": "Verify Cert from LDAP Server" + "VERIFY_CERT": "Verify Cert from LDAP Server", + "REPO_TOOLTIP": "If true, means maintenance in Progress. During this period, you cannot delete repository, tag and push image." }, "LDAP": { "URL": "LDAP URL", @@ -637,7 +639,9 @@ "LABEL_NAME": "Label Name", "COLOR": "Color", "FILTER_Label_PLACEHOLDER": "Filter Labels", - "NO_LABELS": "No labels" + "NO_LABELS": "No labels", + "DELETION_TITLE_TARGET": "Confirm Label Deletion", + "DELETION_SUMMARY_TARGET": "Do you want to delete {{param}}?" }, "WEEKLY": { "MONDAY": "Monday", @@ -650,6 +654,7 @@ }, "UNKNOWN_ERROR": "Ha ocurrido un error desconocido. Por favor, inténtelo de nuevo más tarde.", "UNAUTHORIZED_ERROR": "La sesión no es válida o ha caducado. Necesita identificarse de nuevo para llevar a cabo esa acción.", + "REPO_READ_ONLY": "Maintenance in Progress: During this period, you cannot delete repository, tag and push image.", "FORBIDDEN_ERROR": "No tienes permisos para llevar a cabo esa acción.", "GENERAL_ERROR": "Han ocurrido errores cuando se llamaba al servicio: {{param}}.", "BAD_REQUEST_ERROR": "No hemos podido llevar la acción debido a una solicitud incorrecta.", diff --git a/src/ui_ng/src/i18n/lang/fr-fr-lang.json b/src/ui_ng/src/i18n/lang/fr-fr-lang.json index 0c965db34..4af4c3fc5 100644 --- a/src/ui_ng/src/i18n/lang/fr-fr-lang.json +++ b/src/ui_ng/src/i18n/lang/fr-fr-lang.json @@ -161,6 +161,7 @@ "REPLICATION": "Réplication", "USERS": "Membres", "LOGS": "Logs", + "LABELS": "Labels", "PROJECTS": "Projets", "CONFIG": "Configuration" }, @@ -345,6 +346,7 @@ "DELETION_SUMMARY_TAG": "Voulez-vous supprimer le tag {{param}}?", "DELETION_TITLE_TAG_DENIED": "Un tag signé ne peut être supprimé", "DELETION_SUMMARY_TAG_DENIED": "La balise doit être supprimée du Résumé avant qu'elle ne puisse être supprimée. \nSupprimer du Résumé via cette commande: \n{{param}}", + "TAGS_NO_DELETE": "Upload/Delete is prohibited in read only mode.", "FILTER_FOR_REPOSITORIES": "Filtrer les Dépôts", "TAG": "Tag", "SIZE": "Taille", @@ -390,7 +392,7 @@ "AUTH": "Identification", "REPLICATION": "Réplication", "EMAIL": "Email", - "LABEL": "Label", + "LABEL": "Labels", "SYSTEM": "Réglages Système", "CONFIRM_TITLE": "Confirmer pour annuler", "CONFIRM_SUMMARY": "Certaines modifications n'ont pas été sauvegardées. Voulez-vous les défaire ?", @@ -431,6 +433,7 @@ "PRO_CREATION_RESTRICTION": "L'indicateur pour définir quels utilisateurs ont le droit de créer des projets. Par défaut, tout le monde peut créer un projet. Définissez sur 'Administrateur Seulement' pour que seul un administrateur puisse créer un projet.", "ROOT_CERT_DOWNLOAD": "Téléchargez le certificat racine du dépôt.", "SCANNING_POLICY": "Définissez la politique d'analyse des images en fonction des différentes exigences. 'Aucune' : pas de politique active; 'Tousles jours à' : déclenchement du balayage à l'heure spécifiée tous les jours." + "REPO_TOOLTIP": "If true, means maintenance in Progress. During this period, you cannot delete repository, tag and push image." }, "LDAP": { "URL": "URL LDAP", @@ -562,10 +565,29 @@ "LABEL": { "LABEL": "Label", "DESCRIPTION": "Description", - "CREATION_TIME": "Creation Time" + "CREATION_TIME": "Creation Time", + "NEW_LABEL": "New Label", + "EDIT": "Edit", + "DELETE": "Delete", + "LABEL_NAME": "Label Name", + "COLOR": "Color", + "FILTER_Label_PLACEHOLDER": "Filter Labels", + "NO_LABELS": "No labels", + "DELETION_TITLE_TARGET": "Confirm Label Deletion", + "DELETION_SUMMARY_TARGET": "Do you want to delete {{param}}?" + }, + "WEEKLY": { + "MONDAY": "Monday", + "TUESDAY": "Tuesday", + "WEDNESDAY": "Wednesday", + "THURSDAY": "Thursday", + "FRIDAY": "Friday", + "SATURDAY": "Saturday", + "SUNDAY": "Sunday" }, "UNKNOWN_ERROR": "Des erreurs inconnues sont survenues. Veuillez réessayer plus tard.", "UNAUTHORIZED_ERROR": "Votre session est invalide ou a expiré. Vous devez vous connecter pour continuer votre action.", + "REPO_READ_ONLY": "Maintenance in Progress: During this period, you cannot delete repository, tag and push image.", "FORBIDDEN_ERROR": "Vous n'avez pas les privilèges appropriés pour effectuer l'action.", "GENERAL_ERROR": "Des erreurs sont survenues lors de l'appel à un service : {{param}}.", "BAD_REQUEST_ERROR": "Nous ne pouvons pas exécuter votre action à cause d'une mauvaise requête.", diff --git a/src/ui_ng/src/i18n/lang/zh-cn-lang.json b/src/ui_ng/src/i18n/lang/zh-cn-lang.json index e61f2b90f..6275cae47 100644 --- a/src/ui_ng/src/i18n/lang/zh-cn-lang.json +++ b/src/ui_ng/src/i18n/lang/zh-cn-lang.json @@ -390,6 +390,7 @@ "DELETION_SUMMARY_TAG": "确认删除镜像标签 {{param}}?", "DELETION_TITLE_TAG_DENIED": "已签名的镜像不能被删除", "DELETION_SUMMARY_TAG_DENIED": "要删除此镜像标签必须首先从Notary中删除。\n请执行如下Notary命令删除:\n{{param}}", + "TAGS_NO_DELETE": "在只读模式下删除是被禁止的", "FILTER_FOR_REPOSITORIES": "过滤镜像仓库", "TAG": "标签", "SIZE": "大小", @@ -487,7 +488,8 @@ "PRO_CREATION_RESTRICTION": "用来确定哪些用户有权限创建项目,默认为’所有人‘,设置为’仅管理员‘则只有管理员可以创建项目。", "ROOT_CERT_DOWNLOAD": "下载镜像库根证书.", "SCANNING_POLICY": "基于不同需求设置镜像扫描策略。‘无’:不设置任何策略;‘每日定时’:每天在设置的时间定时执行扫描。", - "VERIFY_CERT": "检查来自LDAP服务端的证书" + "VERIFY_CERT": "检查来自LDAP服务端的证书", + "REPO_TOOLTIP": "选中,表示正在维护状态,不可删除仓库及标签,也不可以推送镜像。" }, "LDAP": { "URL": "LDAP URL", @@ -637,7 +639,9 @@ "LABEL_NAME": "标签名字", "COLOR": "颜色", "FILTER_Label_PLACEHOLDER": "过滤标签", - "NO_LABELS": "无标签" + "NO_LABELS": "无标签", + "DELETION_TITLE_TARGET":"删除标签确认", + "DELETION_SUMMARY_TARGET": "确认删除标签 {{param}}?" }, "WEEKLY": { "MONDAY": "周一", @@ -650,6 +654,7 @@ }, "UNKNOWN_ERROR": "发生未知错误,请稍后再试。", "UNAUTHORIZED_ERROR": "会话无效或者已经过期, 请重新登录以继续。", + "REPO_READ_ONLY": "正在进行维护,在这期间,不能删除仓库、标签,也不能推送镜像。", "FORBIDDEN_ERROR": "当前操作被禁止,请确认你有合法的权限。", "GENERAL_ERROR": "调用后台服务时出现错误: {{param}}。", "BAD_REQUEST_ERROR": "错误请求, 操作无法完成。", diff --git a/tests/resources/Harbor-Pages/Configuration.robot b/tests/resources/Harbor-Pages/Configuration.robot index 8b99584a1..96967eb48 100644 --- a/tests/resources/Harbor-Pages/Configuration.robot +++ b/tests/resources/Harbor-Pages/Configuration.robot @@ -221,6 +221,7 @@ Set Scan All To Daily Click Scan Now click element //vulnerability-config//button[contains(.,'SCAN')] + Enable Read Only ${rc} ${output}= Run And Return Rc And Output curl -u admin:Harbor12345 -s --insecure -H "Content-Type: application/json" -X PUT -d '{"read_only":true}' "https://${ip}/api/configurations" Log To Console ${output} @@ -229,4 +230,46 @@ Enable Read Only Disable Read Only ${rc} ${output}= Run And Return Rc And Output curl -u admin:Harbor12345 -s --insecure -H "Content-Type: application/json" -X PUT -d '{"read_only":false}' "https://${ip}/api/configurations" Log To Console ${output} - Should Be Equal As Integers ${rc} 0 \ No newline at end of file + Should Be Equal As Integers ${rc} 0 + +## System labels +Switch To System Labels + Sleep 1 + Click Element xpath=${configuration_xpath} + Click Element xpath=//*[@id="config-label"] + +Create New Labels + [Arguments] ${labelname} + Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/button[1] + Sleep 1 + Input Text xpath=//*[@id="name"] ${labelname} + Sleep 1 + Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/hbr-create-edit-label/div/form/section/label[2]/clr-dropdown/button + Sleep 1 + Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/hbr-create-edit-label/div/form/section/label[2]/clr-dropdown/clr-dropdown-menu/label[1] + Sleep 1 + Input Text xpath=//*[@id="description"] global + Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/hbr-create-edit-label/div/form/section/label[4]/button[2] + Capture Page Screenshot + Wait Until Page Contains ${labelname} + +Update A Label + [Arguments] ${labelname} + Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[3]/clr-datagrid/div/div/div/clr-dg-table-wrapper/div[2]/clr-dg-row[1]/div/clr-dg-cell[1]/clr-checkbox + Sleep 1 + Click ELement xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/button[2] + Sleep 1 + Input Text xpath=//*[@id="name"] ${labelname} + Sleep 1 + Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/hbr-create-edit-label/div/form/section/label[4]/button[2] + Capture Page Screenshot + Wait Until Page Contains ${labelname} + +Delete A Label + Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[3]/clr-datagrid/div/div/div/clr-dg-table-wrapper/div[2]/clr-dg-row[1]/div/clr-dg-cell[1]/clr-checkbox + Sleep 1 + Click ELement xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/button[3] + Sleep 3 + Capture Page Screenshot + Click Element xpath=//*[@id="system_label"]/hbr-label/div/confirmation-dialog/clr-modal/div/div[1]/div/div[1]/div/div[3]/button[2] + Wait Until Page Contains Deleted successfully