Commit c50cbbf3 authored by AllForNothing's avatar AllForNothing
Browse files

Modify doc and fix some ui bugs


Signed-off-by: default avatarAllForNothing <sshijun@vmware.com>
parent 119751e0
...@@ -3,7 +3,7 @@ title: Configure Project Quotas ...@@ -3,7 +3,7 @@ title: Configure Project Quotas
weight: 25 weight: 25
--- ---
To exercise control over resource use, as a Harbor system administrator you can set quotas on projects. You can limit the number of tags that a project can contain and limit the amount of storage capacity that a project can consume. You can set default quotas that apply to all projects globally. To exercise control over resource use, as a Harbor system administrator you can set quotas on projects. You can limit the amount of storage capacity that a project can consume. You can set default quotas that apply to all projects globally.
{{< note >}} {{< note >}}
Default quotas apply to projects that are created after you set or change the default quota. The default quota is not applied to projects that already existed before you set it. Default quotas apply to projects that are created after you set or change the default quota. The default quota is not applied to projects that already existed before you set it.
...@@ -11,7 +11,7 @@ Default quotas apply to projects that are created after you set or change the de ...@@ -11,7 +11,7 @@ Default quotas apply to projects that are created after you set or change the de
You can also set quotas on individual projects. If you set a global default quota and you set different quotas on individual projects, the per-project quotas are applied. You can also set quotas on individual projects. If you set a global default quota and you set different quotas on individual projects, the per-project quotas are applied.
By default, all projects have unlimited quotas for both tags and storage use. By default, all projects have unlimited quotas for storage use.
1. Select the **Project Quotas** view. 1. Select the **Project Quotas** view.
...@@ -20,14 +20,12 @@ By default, all projects have unlimited quotas for both tags and storage use. ...@@ -20,14 +20,12 @@ By default, all projects have unlimited quotas for both tags and storage use.
![Project quotas](../../img/project-quota2.png) ![Project quotas](../../img/project-quota2.png)
1. For **Default artifact count**, enter the maximum number of tags that any project can contain at a given time, or enter `-1` to set the default to unlimited.
1. For **Default storage consumption**, enter the maximum quantity of storage that any project can consume, selecting `MB`, `GB`, or `TB` from the drop-down menu, or enter `-1` to set the default to unlimited. 1. For **Default storage consumption**, enter the maximum quantity of storage that any project can consume, selecting `MB`, `GB`, or `TB` from the drop-down menu, or enter `-1` to set the default to unlimited.
![Project quotas](../../img/project-quota3.png) ![Project quotas](../../img/project-quota3.png)
1. Click **OK**. 1. Click **OK**.
1. To set quotas on an individual project, click the 3 vertical dots next to a project and select **Edit**. 1. To set quotas on an individual project, select the project and then click **Edit**.
![Project quotas](../../img/project-quota4.png) ![Project quotas](../../img/project-quota4.png)
1. For **Default artifact count**, enter the maximum number of tags that this individual project can contain, or enter `-1` to set the default to unlimited.
1. For **Default storage consumption**, enter the maximum quantity of storage that this individual project can consume, selecting `MB`, `GB`, or `TB` from the drop-down menu. 1. For **Default storage consumption**, enter the maximum quantity of storage that this individual project can consume, selecting `MB`, `GB`, or `TB` from the drop-down menu.
After you set quotas, you can see how much of their quotas each project has consumed. After you set quotas, you can see how much of their quotas each project has consumed.
...@@ -36,18 +34,16 @@ After you set quotas, you can see how much of their quotas each project has cons ...@@ -36,18 +34,16 @@ After you set quotas, you can see how much of their quotas each project has cons
### How Harbor Calculates Resource Usage ### How Harbor Calculates Resource Usage
When setting project quotas, it is useful to know how Harbor calculates tag numbers and storage use, especially in relation to image pushing, retagging, and garbage collection. When setting project quotas, it is useful to know how Harbor calculates storage use, especially in relation to image pushing, retagging, and garbage collection.
- Harbor computes image size when blobs and manifests are pushed from the Docker client. - Harbor computes image size when blobs and manifests are pushed from the Docker client.
- Harbor computes tag counts when manifests are pushed from the Docker client.
{{< note >}} {{< note >}}
When users push an image, the manifest is pushed last, after all of the associated blobs have been pushed successfully to the registry. If several images are pushed concurrently and if there is an insufficient number of tags left in the quota for all of them, images are accepted in the order that their manifests arrive. Consequently, an attempt to push an image might not be immediately rejected for exceeding the quota. This is because there was availability in the tag quota when the push was initiated, but by the time the manifest arrived the quota had been exhausted. When users push an image, the manifest is pushed last, after all of the associated blobs have been pushed successfully to the registry. If several images are pushed concurrently and if there is an insufficient number of tags left in the quota for all of them, images are accepted in the order that their manifests arrive. Consequently, an attempt to push an image might not be immediately rejected for exceeding the quota. This is because there was availability in the tag quota when the push was initiated, but by the time the manifest arrived the quota had been exhausted.
{{< /note >}} {{< /note >}}
- Shared blobs are only computed once per project. In Docker, blob sharing is defined globally. In Harbor, blob sharing is defined at the project level. As a consequence, overall storage usage can be greater than the actual disk capacity. - Shared blobs are only computed once per project. In Docker, blob sharing is defined globally. In Harbor, blob sharing is defined at the project level. As a consequence, overall storage usage can be greater than the actual disk capacity.
- Retagging images reserves and releases resources: - Retagging images reserves and releases resources:
- If you retag an image within a project, the tag count increases by one, but storage usage does not change because there are no new blobs or manifests. - If you retag an image within a project, the storage usage does not change because there are no new blobs or manifests.
- If you retag an image from one project to another, the tag count and storage usage both increase. - If you retag an image from one project to another, the storage usage will increase.
- During garbage collection, Harbor frees the storage used by untagged blobs in the project. - During garbage collection, Harbor frees the storage used by untagged blobs in the project.
- If the tag count reaches the limit, image blobs can be pushed into a project and storage usage is updated accordingly. You can consider these blobs to be untagged blobs. They can be removed by garbage collection, and the storage that they consume is returned after garbage colletion. - Helm chart size is not calculated.
- Helm chart size is not calculated. Only tag counts are calculated.
docs/img/add-robot-account-2.png

16.5 KB | W: | H:

docs/img/add-robot-account-2.png

24.7 KB | W: | H:

docs/img/add-robot-account-2.png
docs/img/add-robot-account-2.png
docs/img/add-robot-account-2.png
docs/img/add-robot-account-2.png
  • 2-up
  • Swipe
  • Onion skin
docs/img/new-robot-account.png

22.8 KB | W: | H:

docs/img/new-robot-account.png

35.8 KB | W: | H:

docs/img/new-robot-account.png
docs/img/new-robot-account.png
docs/img/new-robot-account.png
docs/img/new-robot-account.png
  • 2-up
  • Swipe
  • Onion skin
docs/img/project-quota1.png

55.3 KB | W: | H:

docs/img/project-quota1.png

64.5 KB | W: | H:

docs/img/project-quota1.png
docs/img/project-quota1.png
docs/img/project-quota1.png
docs/img/project-quota1.png
  • 2-up
  • Swipe
  • Onion skin
docs/img/project-quota2.png

8.92 KB | W: | H:

docs/img/project-quota2.png

9.16 KB | W: | H:

docs/img/project-quota2.png
docs/img/project-quota2.png
docs/img/project-quota2.png
docs/img/project-quota2.png
  • 2-up
  • Swipe
  • Onion skin
docs/img/project-quota3.png

16.6 KB | W: | H:

docs/img/project-quota3.png

16.4 KB | W: | H:

docs/img/project-quota3.png
docs/img/project-quota3.png
docs/img/project-quota3.png
docs/img/project-quota3.png
  • 2-up
  • Swipe
  • Onion skin
docs/img/project-quota4.png

8.73 KB | W: | H:

docs/img/project-quota4.png

20.5 KB | W: | H:

docs/img/project-quota4.png
docs/img/project-quota4.png
docs/img/project-quota4.png
docs/img/project-quota4.png
  • 2-up
  • Swipe
  • Onion skin
docs/img/project-quota5.png

9.94 KB | W: | H:

docs/img/project-quota5.png

7.92 KB | W: | H:

docs/img/project-quota5.png
docs/img/project-quota5.png
docs/img/project-quota5.png
docs/img/project-quota5.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -17,6 +17,7 @@ You can create robot accounts to run automated operations. Robot accounts have t ...@@ -17,6 +17,7 @@ You can create robot accounts to run automated operations. Robot accounts have t
1. Click **New Robot Account**. 1. Click **New Robot Account**.
1. Enter a name and an optional description for this robot account. 1. Enter a name and an optional description for this robot account.
1. Set expiration time for this robot account. If not set, the expiration time of system configuration will be used for this robot account.
1. Grant permission to the robot account to push images and to push and pull Helm charts. 1. Grant permission to the robot account to push images and to push and pull Helm charts.
Robot accounts can always pull images, so you cannot deselect this option. Robot accounts can always pull images, so you cannot deselect this option.
......
...@@ -110,8 +110,8 @@ export class GroupComponent implements OnInit, OnDestroy { ...@@ -110,8 +110,8 @@ export class GroupComponent implements OnInit, OnDestroy {
}); });
// batchInfo.id = group.id; // batchInfo.id = group.id;
let deletionMessage = new ConfirmationMessage( let deletionMessage = new ConfirmationMessage(
"MEMBER.DELETION_TITLE", "GROUP.DELETION_TITLE",
"MEMBER.DELETION_SUMMARY", "GROUP.DELETION_SUMMARY",
nameArr.join(","), nameArr.join(","),
this.selectedGroups, this.selectedGroups,
ConfirmationTargets.PROJECT_MEMBER, ConfirmationTargets.PROJECT_MEMBER,
...@@ -185,7 +185,7 @@ export class GroupComponent implements OnInit, OnDestroy { ...@@ -185,7 +185,7 @@ export class GroupComponent implements OnInit, OnDestroy {
} }
get canDeleteGroup(): boolean { get canDeleteGroup(): boolean {
return ( return (
this.selectedGroups.length === 1 && this.selectedGroups.length >= 1 &&
this.session.currentUser.has_admin_role this.session.currentUser.has_admin_role
); );
} }
......
...@@ -161,10 +161,20 @@ ...@@ -161,10 +161,20 @@
<clr-dg-row *ngFor="let artifact of artifactList" [clrDgItem]="artifact" > <clr-dg-row *ngFor="let artifact of artifactList" [clrDgItem]="artifact" >
<clr-dg-cell class="truncated flex-max-width"> <clr-dg-cell class="truncated flex-max-width">
<div class="cell white-normal"> <div class="cell white-normal">
<img class="artifact-icon" [title]="artifact.type" <img *ngIf="artifact?.type !== 'IMAGE';else elseBlock" class="artifact-icon" [title]="artifact.type"
[src]="artifact.type | selectArtifactIcon" /> [src]="artifact.type | selectArtifactIcon" />
&nbsp; &nbsp; <ng-template #elseBlock>
<a href="javascript:void(0)" class="max-width-100" (click)="goIntoArtifactSummaryPage(artifact)" <clr-tooltip>
<div clrTooltipTrigger class="level-border">
<img class="artifact-icon" [title]="artifact.type"
[src]="artifact.type | selectArtifactIcon" />
</div>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>Docker and the Docker logo are trademarks or registered trademarks of Docker, Inc. in the United States and/or other countries.</span>
</clr-tooltip-content>
</clr-tooltip>
</ng-template>
<a href="javascript:void(0)" class="max-width-100 margin-left-5" (click)="goIntoArtifactSummaryPage(artifact)"
title="{{artifact.digest}}"> title="{{artifact.digest}}">
{{ artifact.digest | slice:0:15}}</a> {{ artifact.digest | slice:0:15}}</a>
<clr-tooltip *ngIf="artifact?.references && artifact?.references?.length"> <clr-tooltip *ngIf="artifact?.references && artifact?.references?.length">
...@@ -316,4 +326,4 @@ ...@@ -316,4 +326,4 @@
</clr-dg-footer> </clr-dg-footer>
</clr-datagrid> </clr-datagrid>
</div> </div>
</div> </div>
\ No newline at end of file
...@@ -418,4 +418,7 @@ clr-datagrid { ...@@ -418,4 +418,7 @@ clr-datagrid {
} }
.no-border:focus { .no-border:focus {
outline: none; outline: none;
} }
\ No newline at end of file .margin-left-5 {
margin-left: 5px;
}
...@@ -14,8 +14,23 @@ ...@@ -14,8 +14,23 @@
<clr-icon class="rotate-90 arrow-back" shape="arrow" size="36" (click)="onBack()"></clr-icon> <clr-icon class="rotate-90 arrow-back" shape="arrow" size="36" (click)="onBack()"></clr-icon>
</div> </div>
<div class="title-block"> <div class="title-block">
<h2 class="custom-h2 center-align-items"><img class="artifact-icon" [title]="artifact.type" <h2 class="custom-h2 center-align-items">
[src]="artifact.type | selectArtifactIcon" /> {{artifact?.digest | slice:0:15}} <clr-icon size="25" *ngIf="artifact?.references && artifact?.references?.length" class="icon-folder" shape="folder"></clr-icon></h2> <img *ngIf="artifact?.type !== 'IMAGE';else elseBlock" class="artifact-icon" [title]="artifact.type"
[src]="artifact.type | selectArtifactIcon" />
<ng-template #elseBlock>
<clr-tooltip>
<div clrTooltipTrigger class="level-border">
<img class="artifact-icon" [title]="artifact.type"
[src]="artifact.type | selectArtifactIcon" />
</div>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>Docker and the Docker logo are trademarks or registered trademarks of Docker, Inc. in the United States and/or other countries.</span>
</clr-tooltip-content>
</clr-tooltip>
</ng-template>
<span class="margin-left-10px">{{artifact?.digest | slice:0:15}}</span>
<clr-icon size="25" *ngIf="artifact?.references && artifact?.references?.length" class="icon-folder margin-left-10px" shape="folder"></clr-icon>
</h2>
</div> </div>
</div> </div>
<ng-container *ngIf="!loading"> <ng-container *ngIf="!loading">
......
...@@ -21,13 +21,10 @@ ...@@ -21,13 +21,10 @@
.center-align-items { .center-align-items {
display: flex; display: flex;
align-items: center; align-items: center;
clr-icon {
margin-left: 10px;
}
img {
margin-right: 10px;
}
} }
.artifact-icon { .artifact-icon {
width: 25px; width: 25px;
} }
\ No newline at end of file .margin-left-10px {
margin-left: 10px;
}
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<clr-dg-action-bar> <clr-dg-action-bar>
<div class="clr-row"> <div class="clr-row">
<div class="clr-col-7"> <div class="clr-col-7">
<button id="new-webhook" type="button" class="btn btn-secondary" (click)="newWebhook()"> <button [disabled]="!hasCreatPermission" [clrLoading]="addBtnState" id="new-webhook" type="button" class="btn btn-secondary" (click)="newWebhook()">
<clr-icon shape="plus" size="16"></clr-icon> <clr-icon shape="plus" size="16"></clr-icon>
{{'WEBHOOK.NEW_WEBHOOK' | translate}} {{'WEBHOOK.NEW_WEBHOOK' | translate}}
</button> </button>
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<clr-icon class="clr-icon" shape="caret down"></clr-icon></span> <clr-icon class="clr-icon" shape="caret down"></clr-icon></span>
<clr-dropdown-menu *clrIfOpen> <clr-dropdown-menu *clrIfOpen>
<button clrDropdownItem (click)="switchWebhookStatus()" <button clrDropdownItem (click)="switchWebhookStatus()"
[disabled]="!(selectedRow && selectedRow.length === 1)"> [disabled]="!(selectedRow && selectedRow.length === 1) || !hasUpdatePermission">
<span id="toggle-webhook"> <span id="toggle-webhook">
<span *ngIf="selectedRow[0] && !selectedRow[0].enabled"> <span *ngIf="selectedRow[0] && !selectedRow[0].enabled">
<clr-icon class="margin-top-2" size="16" shape="success-standard"></clr-icon> <clr-icon class="margin-top-2" size="16" shape="success-standard"></clr-icon>
...@@ -26,13 +26,13 @@ ...@@ -26,13 +26,13 @@
</span> </span>
</button> </button>
<button clrDropdownItem (click)="editWebhook()" <button clrDropdownItem (click)="editWebhook()"
class="btn btn-secondary" [disabled]="!(selectedRow && selectedRow.length === 1)"> class="btn btn-secondary" [disabled]="!(selectedRow && selectedRow.length === 1) || !hasUpdatePermission">
<clr-icon class="margin-top-0" size="16" shape="pencil"></clr-icon> <clr-icon class="margin-top-0" size="16" shape="pencil"></clr-icon>
<span id="edit-webhook" class="margin-left-10">{{'BUTTON.EDIT' | translate}}</span> <span id="edit-webhook" class="margin-left-10">{{'BUTTON.EDIT' | translate}}</span>
</button> </button>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<button clrDropdownItem (click)="deleteWebhook()" <button clrDropdownItem (click)="deleteWebhook()"
class="btn btn-secondary" [disabled]="!(selectedRow && selectedRow.length >= 1)"> class="btn btn-secondary" [disabled]="!(selectedRow && selectedRow.length >= 1) || !hasUpdatePermission">
<clr-icon class="margin-top-0" size="16" shape="times"></clr-icon> <clr-icon class="margin-top-0" size="16" shape="times"></clr-icon>
<span id="delete-webhook" <span id="delete-webhook"
class="margin-left-10">{{'BUTTON.DELETE' | translate}}</span> class="margin-left-10">{{'BUTTON.DELETE' | translate}}</span>
......
...@@ -17,6 +17,7 @@ import { AddWebhookFormComponent } from "./add-webhook-form/add-webhook-form.com ...@@ -17,6 +17,7 @@ import { AddWebhookFormComponent } from "./add-webhook-form/add-webhook-form.com
import { InlineAlertComponent } from "../../shared/inline-alert/inline-alert.component"; import { InlineAlertComponent } from "../../shared/inline-alert/inline-alert.component";
import { AddWebhookComponent } from "./add-webhook/add-webhook.component"; import { AddWebhookComponent } from "./add-webhook/add-webhook.component";
import { ConfirmationDialogComponent } from "../../../lib/components/confirmation-dialog"; import { ConfirmationDialogComponent } from "../../../lib/components/confirmation-dialog";
import { UserPermissionService } from '../../../lib/services';
describe('WebhookComponent', () => { describe('WebhookComponent', () => {
let component: WebhookComponent; let component: WebhookComponent;
let fixture: ComponentFixture<WebhookComponent>; let fixture: ComponentFixture<WebhookComponent>;
...@@ -92,6 +93,11 @@ describe('WebhookComponent', () => { ...@@ -92,6 +93,11 @@ describe('WebhookComponent', () => {
} }
} }
}; };
const mockUserPermissionService = {
getPermission() {
return of(true).pipe(delay(0));
}
};
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
...@@ -118,6 +124,7 @@ describe('WebhookComponent', () => { ...@@ -118,6 +124,7 @@ describe('WebhookComponent', () => {
{ provide: WebhookService, useValue: mockWebhookService }, { provide: WebhookService, useValue: mockWebhookService },
{ provide: MessageHandlerService, useValue: mockMessageHandlerService }, { provide: MessageHandlerService, useValue: mockMessageHandlerService },
{ provide: ActivatedRoute, useValue: mockActivatedRoute }, { provide: ActivatedRoute, useValue: mockActivatedRoute },
{ provide: UserPermissionService, useValue: mockUserPermissionService },
] ]
}) })
.compileComponents(); .compileComponents();
......
...@@ -11,26 +11,24 @@ ...@@ -11,26 +11,24 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { finalize } from "rxjs/operators"; import { finalize } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { AddWebhookComponent } from "./add-webhook/add-webhook.component"; import { AddWebhookComponent } from './add-webhook/add-webhook.component';
import { AddWebhookFormComponent } from "./add-webhook-form/add-webhook-form.component"; import { AddWebhookFormComponent } from './add-webhook-form/add-webhook-form.component';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Webhook, LastTrigger } from './webhook'; import { LastTrigger, Webhook } from './webhook';
import { WebhookService } from './webhook.service'; import { WebhookService } from './webhook.service';
import { MessageHandlerService } from "../../shared/message-handler/message-handler.service"; import { MessageHandlerService } from '../../shared/message-handler/message-handler.service';
import { Project } from '../project'; import { Project } from '../project';
import { import { ConfirmationButtons, ConfirmationState, ConfirmationTargets } from '../../shared/shared.const';
ConfirmationTargets,
ConfirmationState,
ConfirmationButtons
} from "../../shared/shared.const";
import { ConfirmationMessage } from "../../shared/confirmation-dialog/confirmation-message"; import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message';
import { ConfirmationDialogComponent } from "../../shared/confirmation-dialog/confirmation-dialog.component"; import { ConfirmationDialogComponent } from '../../shared/confirmation-dialog/confirmation-dialog.component';
import { clone } from "../../../lib/utils/utils"; import { clone } from '../../../lib/utils/utils';
import { forkJoin, Observable } from "rxjs"; import { forkJoin, Observable } from 'rxjs';
import { UserPermissionService, USERSTATICPERMISSION } from '../../../lib/services';
import { ClrLoadingState } from '@clr/angular';
@Component({ @Component({
templateUrl: './webhook.component.html', templateUrl: './webhook.component.html',
...@@ -53,11 +51,15 @@ export class WebhookComponent implements OnInit { ...@@ -53,11 +51,15 @@ export class WebhookComponent implements OnInit {
loadingMetadata: boolean = false; loadingMetadata: boolean = false;
loadingWebhookList: boolean = false; loadingWebhookList: boolean = false;
loadingTriggers: boolean = false; loadingTriggers: boolean = false;
hasCreatPermission: boolean = false;
hasUpdatePermission: boolean = false;
addBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private translate: TranslateService, private translate: TranslateService,
private webhookService: WebhookService, private webhookService: WebhookService,
private messageHandlerService: MessageHandlerService) { } private messageHandlerService: MessageHandlerService,
private userPermissionService: UserPermissionService,) { }
ngOnInit() { ngOnInit() {
this.projectId = +this.route.snapshot.parent.params['id']; this.projectId = +this.route.snapshot.parent.params['id'];
...@@ -67,6 +69,22 @@ export class WebhookComponent implements OnInit { ...@@ -67,6 +69,22 @@ export class WebhookComponent implements OnInit {
this.projectName = project.name; this.projectName = project.name;
} }
this.getData(); this.getData();
this.getPermissions();
}
getPermissions() {
const permissionsList: Observable<boolean>[] = [];
permissionsList.push(this.userPermissionService.getPermission(this.projectId,
USERSTATICPERMISSION.WEBHOOK.KEY, USERSTATICPERMISSION.WEBHOOK.VALUE.CREATE));
permissionsList.push(this.userPermissionService.getPermission(this.projectId,
USERSTATICPERMISSION.WEBHOOK.KEY, USERSTATICPERMISSION.WEBHOOK.VALUE.UPDATE));
this.addBtnState = ClrLoadingState.LOADING;
forkJoin(...permissionsList).subscribe(Rules => {
[this.hasCreatPermission, this.hasUpdatePermission] = Rules;
this.addBtnState = ClrLoadingState.SUCCESS;
}, error => {
this.messageHandlerService.error(error);
this.addBtnState = ClrLoadingState.ERROR;
});
} }
getData() { getData() {
......
...@@ -437,7 +437,9 @@ ...@@ -437,7 +437,9 @@
"PROJECT_MASTER": "Master", "PROJECT_MASTER": "Master",
"DEVELOPER": "Developer", "DEVELOPER": "Developer",
"GUEST": "Guest", "GUEST": "Guest",
"LIMITED_GUEST": "Limited Guest" "LIMITED_GUEST": "Limited Guest",
"DELETION_TITLE": "Confirm group members deletion",
"DELETION_SUMMARY": "Do you want to delete group member(s) {{param}}?"
}, },
"AUDIT_LOG": { "AUDIT_LOG": {
"USERNAME": "Username", "USERNAME": "Username",
......
...@@ -437,7 +437,9 @@ ...@@ -437,7 +437,9 @@
"PROJECT_MASTER": "Master", "PROJECT_MASTER": "Master",
"DEVELOPER": "Developer", "DEVELOPER": "Developer",
"GUEST": "Guest", "GUEST": "Guest",
"LIMITED_GUEST": "Limited Guest" "LIMITED_GUEST": "Limited Guest",
"DELETION_TITLE": "Confirm group members deletion",
"DELETION_SUMMARY": "Do you want to delete group member(s) {{param}}?"
}, },
"AUDIT_LOG": { "AUDIT_LOG": {
"USERNAME": "Nombre de usuario", "USERNAME": "Nombre de usuario",
......
...@@ -429,7 +429,9 @@ ...@@ -429,7 +429,9 @@
"PROJECT_MASTER": "Master", "PROJECT_MASTER": "Master",
"DEVELOPER": "Developer", "DEVELOPER": "Developer",
"GUEST": "Guest", "GUEST": "Guest",
"LIMITED_GUEST": "Limited Guest" "LIMITED_GUEST": "Limited Guest",
"DELETION_TITLE": "Confirm group members deletion",
"DELETION_SUMMARY": "Do you want to delete group member(s) {{param}}?"
}, },
"AUDIT_LOG": { "AUDIT_LOG": {
"USERNAME": "Nom d'utilisateur", "USERNAME": "Nom d'utilisateur",
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment