cy.get('#owner').should('have.class', 'ng-valid');
if (isLocking) {
- cy.get('#lock_enabled').click({ force: true });
+ cy.get('#lock_enabled_input').click({ force: true });
// Select lock mode:
this.selectLockMode('Compliance');
- cy.get('#lock_mode').should('have.class', 'ng-valid');
cy.get('#lock_retention_period_days').type('3');
}
// If object locking is enabled versioning shouldn't be visible
if (isLocking) {
- cy.get('input[id=versioning]').should('be.disabled');
+ cy.get('input[name=versioning]').should('be.disabled');
cy.contains('button', 'Edit Bucket').click();
this.getTableCell(this.columnIndex.name, name)
return cy.get('@versioningValueCell').should('have.text', this.versioningStateEnabled);
}
// Enable versioning
- cy.get('input[id=versioning]').should('not.be.checked');
- cy.get('label[for=versioning]').click();
- cy.get('input[id=versioning]').should('be.checked');
+ cy.get('input[name=versioning]').should('not.be.checked');
+ cy.get('label[for=versioning_input]').click();
+ cy.get('input[name=versioning]').should('be.checked');
cy.contains('button', 'Edit Bucket').click();
// Check if the owner is updated
// Disable versioning:
this.navigateEdit(name, false, true, null, true);
- cy.get('label[for=versioning]').click();
- cy.get('input[id=versioning]').should('not.be.checked');
+ cy.get('label[for=versioning_input]').click();
+ cy.get('input[name=versioning]').should('not.be.checked');
cy.contains('button', 'Edit Bucket').wait(WAIT_TIMER).click();
// Check versioning suspended:
.and('have.class', 'ng-invalid');
// Check that error message was printed under name input field
- cy.get('#bid + .invalid-feedback').should(
- 'have.text',
- 'Bucket names must be 3 to 63 characters long.'
- );
+ cy.get('cds-text-label[for=bid]')
+ .find('span.invalid-feedback')
+ .should('have.text', 'Bucket names must be 3 to 63 characters long.');
// Test invalid owner input
// select some valid option. The owner drop down error message will not appear unless a valid user was selected at
cy.get('@nameInputField').click();
// Check that owner drop down field was marked invalid in the css
- cy.get('#owner').should('have.class', 'ng-invalid');
+ cy.get('cds-select[id=owner]').should('have.class', 'ng-invalid');
// Check that error message was printed under owner drop down field
- cy.get('#owner + .invalid-feedback').should('have.text', 'This field is required.');
+ cy.get('cds-select[id=owner]')
+ .find('.invalid-feedback')
+ .should('have.text', 'This field is required.');
// Clicks the Create Bucket button but the page doesn't move.
// Done by testing for the breadcrumb
testInvalidEdit(name: string) {
this.navigateEdit(name, false, true, null, true);
- cy.get('input[id=versioning]').should('exist').and('not.be.checked');
+ cy.get('input[name=versioning]').should('exist').and('not.be.checked');
// Chooses 'Select a user' rather than a valid owner on Edit Bucket page
// and checks if it's an invalid input
cy.contains('button', 'Edit Bucket').click();
// Check that owner drop down field was marked invalid in the css
- cy.get('#owner').should('have.class', 'ng-invalid');
+ cy.get('cds-select[id=owner]').should('have.class', 'ng-invalid');
// Check that error message was printed under owner drop down field
- cy.get('#owner + .invalid-feedback').should('have.text', 'This field is required.');
+ cy.get('cds-select[id=owner]')
+ .find('.invalid-feedback')
+ .should('have.text', 'This field is required.');
this.expectBreadcrumbText('Edit');
}
// Enter in user_id
cy.get('#user_id').type(user_id);
// Show Tenant
- cy.get('#show_tenant').click({ force: true });
+ cy.get('#show_tenant_input').click({ force: true });
// Enter in tenant
cy.get('#tenant').type(tenant);
// Enter in full name
// Enter max buckets
this.selectOption('max_buckets_mode', 'Custom');
- cy.get('#max_buckets').should('exist').should('have.value', '1000');
- cy.get('#max_buckets').click().clear().type(maxbuckets);
+ cy.get('input#max_buckets').should('exist').should('have.value', '1000');
+ cy.get('input#max_buckets').click().clear().type(maxbuckets);
// Click the create button and wait for user to be made
cy.contains('button', 'Create User').click();
this.navigateEdit(name, false, true, null, true);
// Change the full name field
- cy.get('#display_name').click().clear().type(new_fullname);
+ cy.get('input#display_name').click().clear({ force: true }).type(new_fullname, { force: true });
// Change the email field
cy.get('#email').click().clear().type(new_email);
// Change the max buckets field
this.selectOption('max_buckets_mode', 'Custom');
- cy.get('#max_buckets').click().clear().type(new_maxbuckets);
+ cy.get('input#max_buckets').click().clear().type(new_maxbuckets);
cy.contains('button', 'Edit User').click();
.should('have.class', 'ng-invalid')
// Try to give user already taken name. Should make field invalid.
.type(uname);
- cy.get('#show_tenant').click({ force: true });
+ cy.get('#show_tenant_input').click({ force: true });
cy.get('#tenant').type(tenant).should('have.class', 'ng-invalid');
- cy.contains('#tenant + .invalid-feedback', 'The chosen user ID exists in this tenant.');
+ cy.get('cds-text-label[for=tenant]')
+ .find('.invalid-feedback')
+ .should('have.text', 'The chosen user ID exists in this tenant.');
// check that username field is marked invalid if username has been cleared off
cy.get('#user_id').clear().blur().should('have.class', 'ng-invalid');
- cy.contains('#user_id + .invalid-feedback', 'This field is required.');
+ cy.get('cds-text-label[for=user_id]')
+ .find('.invalid-feedback')
+ .should('have.text', 'This field is required.');
// Full name
cy.get('#display_name')
.clear()
.blur()
.should('have.class', 'ng-invalid');
- cy.contains('#display_name + .invalid-feedback', 'This field is required.');
+ cy.get('cds-text-label[for=display_name]')
+ .find('.invalid-feedback')
+ .should('have.text', 'This field is required.');
// put invalid email to make field invalid
cy.get('#email').type('a').blur().should('have.class', 'ng-invalid');
- cy.contains('#email + .invalid-feedback', 'This is not a valid email address.');
+ cy.get('cds-text-label[for=email]')
+ .find('.invalid-feedback')
+ .should('have.text', 'This is not a valid email address.');
// put negative max buckets to make field invalid
this.expectSelectOption('max_buckets_mode', 'Custom');
- cy.get('#max_buckets').clear().type('-5').blur().should('have.class', 'ng-invalid');
- cy.contains('#max_buckets + .invalid-feedback', 'The entered value must be >= 1.');
+ cy.get('input#max_buckets').clear().type('-5').blur();
+ cy.get('#max_buckets').should('have.class', 'ng-invalid');
+ cy.get('cds-number[for=max_buckets]')
+ .find('.invalid-feedback')
+ .should('have.text', 'The entered value must be >= 1.');
this.navigateTo();
this.delete(tenant + '$' + uname, null, null, true, true);
.blur()
.should('not.have.class', 'ng-pending')
.should('have.class', 'ng-invalid');
- cy.contains('#email + .invalid-feedback', 'This is not a valid email address.');
+
+ cy.get('cds-text-label[for=email]')
+ .find('.invalid-feedback')
+ .should('have.text', 'This is not a valid email address.');
// empty the display name field making it invalid
- cy.get('#display_name').clear().blur().should('have.class', 'ng-invalid');
- cy.contains('#display_name + .invalid-feedback', 'This field is required.');
+ cy.get('#display_name').clear({ force: true }).blur().should('have.class', 'ng-invalid');
+ cy.get('cds-text-label[for=display_name]')
+ .find('.invalid-feedback')
+ .should('have.text', 'This field is required.');
// put negative max buckets to make field invalid
this.selectOption('max_buckets_mode', 'Disabled');
- cy.get('#max_buckets').should('not.exist');
+ cy.get('input#max_buckets').should('not.exist');
this.selectOption('max_buckets_mode', 'Custom');
- cy.get('#max_buckets').should('exist').should('have.value', '50');
- cy.get('#max_buckets').clear().type('-5').blur().should('have.class', 'ng-invalid');
- cy.contains('#max_buckets + .invalid-feedback', 'The entered value must be >= 1.');
+ cy.get('input#max_buckets').should('exist').should('have.value', '50');
+ cy.get('input#max_buckets').clear().type('-5').blur();
+ cy.get('#max_buckets').should('have.class', 'ng-invalid');
+ cy.get('cds-number[for=max_buckets]')
+ .find('.invalid-feedback')
+ .should('have.text', 'The entered value must be >= 1.');
this.navigateTo();
this.delete(tenant + '$' + uname, null, null, true, true);
-<cd-modal [modalRef]="activeModal">
- <span class="modal-title"
- i18n>{{ getMode() }} Tag</span>
+<cds-modal size="md"
+ [open]="open"
+ [hasScrollingContent]="false"
+ (overlaySelected)="closeModal()">
- <ng-container class="modal-content">
- <form class="form"
- #formDir="ngForm"
- [formGroup]="form">
- <div class="modal-body">
- <!-- Key -->
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="key"
- i18n>Key</label>
- <div class="cd-col-form-input">
- <input type="text"
- class="form-control"
- formControlName="key"
- id="key">
- <span class="invalid-feedback"
- *ngIf="form.showError('key', formDir, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="form.showError('key', formDir, 'unique')"
- i18n>This key must be unique.</span>
- <span class="invalid-feedback"
- *ngIf="form.showError('key', formDir, 'maxLength')"
- i18n>Length of the key must be maximum of 128 characters</span>
- </div>
- </div>
+ <cds-modal-header (closeSelect)="closeModal()">
+ <h3 cdsModalHeaderHeading
+ i18n>{{ getMode() }} Tag</h3>
+ </cds-modal-header>
- <!-- Value -->
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="value"
- i18n>Value</label>
- <div class="cd-col-form-input">
- <input id="value"
- class="form-control"
- type="text"
- formControlName="value">
- <span *ngIf="form.showError('value', formDir, 'required')"
- class="invalid-feedback"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="form.showError('value', formDir, 'maxLength')"
- i18n>Length of the value must be a maximum of 128 characters</span>
- </div>
- </div>
- </div>
+ <form class="form"
+ #formDir="ngForm"
+ [formGroup]="form">
+ <div cdsModalContent>
+ <!-- Key -->
+ <div class="form-item">
+ <cds-text-label label="Key"
+ for="key"
+ cdRequiredField="Key"
+ [invalid]="form.controls.key.invalid && form.controls.key.dirty"
+ [invalidText]="keyError">Key
+ <input cdsText
+ type="text"
+ id="key"
+ name="key"
+ formControlName="key"
+ [invalid]="form.controls.key.invalid && form.controls.key.dirty"
+ [autofocus]="true"
+ modal-primary-focus>
+ </cds-text-label>
+ <ng-template #keyError>
+ <span class="invalid-feedback"
+ *ngIf="form.showError('key', formDir, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="form.showError('key', formDir, 'unique')"
+ i18n>This key must be unique.</span>
+ <span class="invalid-feedback"
+ *ngIf="form.showError('key', formDir, 'maxLength')"
+ i18n>Length of the key must be maximum of 128 characters</span>
+ </ng-template>
+ </div>
- <div class="modal-footer">
- <cd-form-button-panel (submitActionEvent)="onSubmit()"
- [form]="form"
- [submitText]="getMode()"></cd-form-button-panel>
- </div>
- </form>
- </ng-container>
- </cd-modal>
+ <!-- Value -->
+ <div class="form-item">
+ <cds-text-label label="Value"
+ for="value"
+ cdRequiredField="Value"
+ [invalid]="form.controls.value.invalid && form.controls.value.dirty"
+ [invalidText]="valueError">Value
+ <input cdsText
+ type="text"
+ id="value"
+ name="value"
+ formControlName="value"
+ [invalid]="form.controls.value.invalid && form.controls.value.dirty">
+ </cds-text-label>
+ <ng-template #valueError>
+ <span class="invalid-feedback"
+ *ngIf="form.showError('value', formDir, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="form.showError('value', formDir, 'maxLength')"
+ i18n>Length of the value must be a maximum of 128 characters</span>
+ </ng-template>
+ </div>
+
+ </div>
+
+ <cd-form-button-panel (submitActionEvent)="onSubmit()"
+ [form]="form"
+ [submitText]="getMode()"
+ [modalForm]="true"></cd-form-button-panel>
+ </form>
+</cds-modal>
import { Component, EventEmitter, Output } from '@angular/core';
import { Validators } from '@angular/forms';
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { BaseModal } from 'carbon-components-angular';
import _ from 'lodash';
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder';
templateUrl: './bucket-tag-modal.component.html',
styleUrls: ['./bucket-tag-modal.component.scss']
})
-export class BucketTagModalComponent {
+export class BucketTagModalComponent extends BaseModal {
@Output()
submitAction = new EventEmitter();
currentKeyTags: string[];
storedKey: string;
- constructor(
- private formBuilder: CdFormBuilder,
- public activeModal: NgbActiveModal,
- public actionLabels: ActionLabelsI18n
- ) {
+ constructor(private formBuilder: CdFormBuilder, public actionLabels: ActionLabelsI18n) {
+ super();
this.createForm();
}
onSubmit() {
this.submitAction.emit(this.form.value);
- this.activeModal.close();
+ this.closeModal();
}
getMode() {
-<div class="cd-col-form"
+<div cdsCol
+ [columnNumbers]="{md: 4}"
*cdFormLoading="loading">
<form name="bucketForm"
#frm="ngForm"
[formGroup]="bucketForm"
novalidate>
- <div class="card">
- <div i18n="form title"
- class="card-header">{{ action | titlecase }} {{ resource | upperFirst }}</div>
- <div class="card-body">
- <!-- Id -->
- <div class="form-group row"
- *ngIf="editing">
- <label i18n
- class="cd-col-form-label"
- for="id">Id</label>
- <div class="cd-col-form-input">
- <input id="id"
- name="id"
- class="form-control"
- type="text"
- formControlName="id"
- readonly>
- </div>
- </div>
+ <div i18n="form title"
+ class="form-header">{{ action | titlecase }} {{ resource | upperFirst }}</div>
- <!-- Name -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- [ngClass]="{required: !editing}"
- for="bid"
- i18n>Name</label>
- <div class="cd-col-form-input">
- <input id="bid"
- name="bid"
- class="form-control"
- type="text"
- i18n-placeholder
- placeholder="Name..."
- formControlName="bid"
- [readonly]="editing"
- [autofocus]="!editing">
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('bid', frm, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('bid', frm, 'bucketNameInvalid')"
- i18n>Bucket names can only contain lowercase letters, numbers, periods and hyphens.</span>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('bid', frm, 'bucketNameNotAllowed')"
- i18n>The chosen name is already in use.</span>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('bid', frm, 'containsUpperCase')"
- i18n>Bucket names must not contain uppercase characters or underscores.</span>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('bid', frm, 'lowerCaseOrNumber')"
- i18n>Each label must start and end with a lowercase letter or a number.</span>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('bid', frm, 'ipAddress')"
- i18n>Bucket names cannot be formatted as IP address.</span>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('bid', frm, 'onlyLowerCaseAndNumbers')"
- i18n>Bucket labels cannot be empty and can only contain lowercase letters, numbers and hyphens.</span>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('bid', frm, 'shouldBeInRange')"
- i18n>Bucket names must be 3 to 63 characters long.</span>
- </div>
- </div>
+ <!-- Id -->
+ <div class="form-item"
+ *ngIf="editing">
+ <cds-text-label for="id"
+ i18n>Id
+ <input cdsText
+ id="id"
+ name="id"
+ formControlName="id"
+ readonly>
+ </cds-text-label>
+ </div>
- <!-- Owner -->
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="owner"
- i18n>Owner</label>
- <div class="cd-col-form-input">
- <select id="owner"
- name="owner"
- class="form-select"
- formControlName="owner"
- [autofocus]="editing">
- <option i18n
- *ngIf="owners === null"
- [ngValue]="null">Loading...</option>
- <option i18n
- *ngIf="owners !== null"
- [ngValue]="null">-- Select a user --</option>
- <option *ngFor="let owner of owners"
- [value]="owner">{{ owner }}</option>
- </select>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('owner', frm, 'required')"
- i18n>This field is
- required.</span>
- <cd-alert-panel type="info"
- *ngIf="bucketForm.get('owner').disabled"
- spacingClass="me-1 mt-1"
- i18n>The bucket is owned by an account. UI does not support changing the ownership of bucket owned by an account.
- </cd-alert-panel>
- </div>
- </div>
+ <!-- Name -->
+ <div class="form-item">
+ <cds-text-label for="bid"
+ cdRequiredField="Name"
+ [invalid]="!bucketForm.controls.bid.valid && bucketForm.controls.bid.dirty"
+ [invalidText]="nameError"
+ i18n>Name
+ <input cdsText
+ placeholder="Name..."
+ i18n-placeholder
+ id="bid"
+ name="bid"
+ formControlName="bid"
+ [readonly]="editing"
+ [autofocus]="!editing"
+ [invalid]="!bucketForm.controls.bid.valid && bucketForm.controls.bid.dirty">
+ </cds-text-label>
+ <ng-template #nameError>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('bid', frm, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('bid', frm, 'bucketNameInvalid')"
+ i18n>Bucket names can only contain lowercase letters, numbers, periods and hyphens.</span>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('bid', frm, 'bucketNameNotAllowed')"
+ i18n>The chosen name is already in use.</span>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('bid', frm, 'containsUpperCase')"
+ i18n>Bucket names must not contain uppercase characters or underscores.</span>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('bid', frm, 'lowerCaseOrNumber')"
+ i18n>Each label must start and end with a lowercase letter or a number.</span>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('bid', frm, 'ipAddress')"
+ i18n>Bucket names cannot be formatted as IP address.</span>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('bid', frm, 'onlyLowerCaseAndNumbers')"
+ i18n>Bucket labels cannot be empty and can only contain lowercase letters, numbers and hyphens.</span>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('bid', frm, 'shouldBeInRange')"
+ i18n>Bucket names must be 3 to 63 characters long.</span>
+ </ng-template>
+ </div>
- <!-- Versioning -->
- <fieldset *ngIf="editing">
- <legend class="cd-header"
- i18n>Versioning</legend>
+ <!-- Owner -->
+ <div class="form-item">
+ <cds-select label="Owner"
+ for="owner"
+ formControlName="owner"
+ name="owner"
+ id="owner"
+ [invalidText]="ownerError"
+ [invalid]="!bucketForm.controls.owner.valid && bucketForm.controls.owner.dirty"
+ cdRequiredField="Owner"
+ i18n>Owner
+ <option *ngIf="owners === null"
+ [ngValue]="null">Loading...</option>
+ <option *ngIf="owners !== null"
+ value="">-- Select a user --</option>
+ <option *ngFor="let owner of owners"
+ [value]="owner">{{ owner }}</option>
+ </cds-select>
+ <ng-template #ownerError>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('owner', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
+ <cd-alert-panel
+ type="info"
+ *ngIf="bucketForm.get('owner').disabled"
+ spacingClass="me-1 mt-1"
+ i18n>
+ The bucket is owned by an account. UI does not support changing
+ the ownership of bucket owned by an account.
+ </cd-alert-panel>
+ </div>
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input type="checkbox"
- class="custom-control-input"
- id="versioning"
- name="versioning"
- formControlName="versioning"
- (change)="setMfaDeleteValidators()">
- <label class="custom-control-label spacing-03"
- for="versioning"
- i18n>Enabled</label>
- <cd-helper>
- <span i18n>Enables versioning for the objects in the bucket.</span>
- </cd-helper>
- </div>
- </div>
- </div>
- </fieldset>
+ <!-- Versioning -->
+ <fieldset *ngIf="editing">
+ <div class="form-item">
+ <legend class="cd-header"
+ i18n>Versioning</legend>
- <!-- Multi-Factor Authentication -->
- <fieldset *ngIf="editing">
- <!-- MFA Delete -->
- <legend class="cd-header"
- i18n>Multi-Factor Authentication</legend>
+ <cds-checkbox name="versioning"
+ formControlName="versioning"
+ id="versioning"
+ (checkedChange)="setMfaDeleteValidators()"
+ i18n>Enabled
+ <cd-help-text>
+ <span>Enables versioning for the objects in the bucket.</span>
+ </cd-help-text>
+ </cds-checkbox>
+ </div>
+ </fieldset>
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input type="checkbox"
- class="custom-control-input"
- id="mfa-delete"
- name="mfa-delete"
- formControlName="mfa-delete"
- (change)="setMfaDeleteValidators()">
- <label class="custom-control-label"
- for="mfa-delete"
- i18n>Delete enabled</label>
- <cd-helper>
- <span i18n>Enables MFA (multi-factor authentication) Delete, which requires additional authentication for changing the bucket versioning state.</span>
- </cd-helper>
- </div>
- </div>
- </div>
- <div *ngIf="areMfaCredentialsRequired()"
- class="form-group row">
- <label i18n
- class="cd-col-form-label"
- for="mfa-token-serial">Token Serial Number</label>
- <div class="cd-col-form-input">
- <input type="text"
- id="mfa-token-serial"
- name="mfa-token-serial"
- formControlName="mfa-token-serial"
- class="form-control">
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('mfa-token-serial', frm, 'required')"
- i18n>This field is required.</span>
- </div>
- </div>
- <div *ngIf="areMfaCredentialsRequired()"
- class="form-group row">
- <label i18n
- class="cd-col-form-label"
- for="mfa-token-pin">Token PIN</label>
- <div class="cd-col-form-input">
- <input type="text"
- id="mfa-token-pin"
- name="mfa-token-pin"
- formControlName="mfa-token-pin"
- class="form-control">
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('mfa-token-pin', frm, 'required')"
- i18n>This field is required.</span>
- </div>
- </div>
- </fieldset>
+ <!-- Multi-Factor Authentication -->
+ <fieldset *ngIf="editing">
+ <!-- MFA Delete -->
+ <div class="form-item">
+ <legend class="cd-header"
+ i18n>Multi-Factor Authentication</legend>
- <!-- Object Locking -->
- <fieldset *ngIf="!editing || (editing && bucketForm.getValue('lock_enabled'))">
- <legend class="cd-header"
- i18n>Object Locking
- <cd-help-text>
- Store objects using a write-once-read-many (WORM) model to prevent objects from being deleted or
- overwritten for a fixed amount of time or indefinitely.
+ <cds-checkbox name="mfa-delete"
+ formControlName="mfa-delete"
+ id="mfa-delete"
+ (checkedChange)="setMfaDeleteValidators()"
+ helperText="Enables MFA (multi-factor authentication) Delete, which requires additional authentication for changing the bucket versioning state."
+ i18n-helperText
+ i18n>Delete enabled
+ </cds-checkbox>
+ </div>
+ <div *ngIf="areMfaCredentialsRequired()"
+ class="form-item">
+ <cds-text-label for="mfa-token-serial"
+ cdRequiredField="MFA Serial Number"
+ [invalid]="!bucketForm.controls['mfa-token-serial'].valid && bucketForm.controls['mfa-token-serial'].dirty"
+ [invalidText]="mfaSerialError"
+ i18n>Token Serial Number
+ <input cdsText
+ id="mfa-token-serial"
+ name="mfa-token-serial"
+ formControlName="mfa-token-serial"
+ [invalid]="!bucketForm.controls['mfa-token-serial'].valid && bucketForm.controls['mfa-token-serial'].dirty">
+ </cds-text-label>
+ <ng-template #mfaSerialError>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('mfa-token-serial', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
+ </div>
+ <div *ngIf="areMfaCredentialsRequired()"
+ class="form-item">
+ <cds-text-label for="mfa-token-pin"
+ cdRequiredField="MFA Token PIN"
+ [invalid]="!bucketForm.controls['mfa-token-pin'].valid && (bucketForm.controls['mfa-token-pin'].dirty)"
+ [invalidText]="mfaPinError"
+ i18n>Token PIN
+ <input cdsText
+ id="mfa-token-pin"
+ name="mfa-token-pin"
+ formControlName="mfa-token-pin"
+ [invalid]="!bucketForm.controls['mfa-token-pin'].valid && (bucketForm.controls['mfa-token-pin'].dirty)">
+ </cds-text-label>
+ <ng-template #mfaPinError>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('mfa-token-pin', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
+ </div>
+ </fieldset>
+
+ <!-- Object Locking -->
+ <fieldset *ngIf="!editing || (editing && bucketForm.getValue('lock_enabled'))">
+ <div class="form-item">
+ <legend class="cd-header"
+ i18n>
+ Object Locking
+ <cd-help-text>
+ Store objects using a write-once-read-many (WORM) model to prevent objects from being deleted or overwritten for a fixed amount of time or indefinitely.
Object Locking works only in versioned buckets.
- </cd-help-text>
- </legend>
- <!-- Object Locking enable -->
- <div class="form-group row">
- <label class="cd-col-form-label pt-0"
- for="lock_enabled"
- i18n>
- Enable
- </label>
- <div class="cd-col-form-input">
- <input class="form-check-input"
- id="lock_enabled"
- formControlName="lock_enabled"
- type="checkbox" />
- <cd-help-text>
- <span i18n>Enables locking for the objects in the bucket. Locking can only be enabled while creating a bucket.</span>
- </cd-help-text>
- </div>
- </div>
- <!-- Object Locking mode -->
- <div *ngIf="bucketForm.getValue('lock_enabled')"
- class="form-group row">
- <label class="cd-col-form-label"
- for="lock_mode"
- i18n>Mode</label>
- <div class="cd-col-form-input">
- <select class="form-select"
- formControlName="lock_mode"
- name="lock_mode"
- id="lock_mode">
- <option i18n
- value="COMPLIANCE">
- Compliance
- </option>
- <option i18n
- value="GOVERNANCE">
- Governance
- </option>
- </select>
- <cd-help-text>
- <span *ngIf="bucketForm.getValue('lock_mode') === 'COMPLIANCE'"
- i18n>In COMPLIANCE an object version cannot be overwritten or deleted for the duration of the period.
- </span>
- <span *ngIf="bucketForm.getValue('lock_mode') === 'GOVERNANCE'"
- i18n>In GOVERNANCE mode, users cannot overwrite or delete an object version or alter its lock settings unless they have special permissions.
- </span>
- </cd-help-text>
- </div>
- </div>
- <!-- Retention period (days) -->
- <div *ngIf="bucketForm.getValue('lock_enabled')"
- class="form-group row">
- <label class="cd-col-form-label"
- for="lock_retention_period_days">
- <ng-container i18n>Days</ng-container>
- </label>
- <div class="cd-col-form-input">
- <input class="form-control"
- type="number"
- id="lock_retention_period_days"
- formControlName="lock_retention_period_days"
- min="1">
- <cd-help-text>
- <span i18n>The number of days that you want to specify for the default retention period that will be
- applied to new objects placed in this bucket.</span>
- </cd-help-text>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('lock_retention_period_days', frm, 'pattern')"
- i18n>The entered value must be a positive integer.</span>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('lock_retention_period_days', frm, 'lockDays')"
- i18n>Retention Days must be a positive integer.</span>
- </div>
- </div>
- <!-- Alerts -->
- <div class="form-group row">
- <div class="cd-col-form-label"></div>
- <div class="cd-col-form-input">
- <cd-alert-panel type="info"
- *ngIf="bucketForm.getValue('lock_enabled')"
- class="me-1"
- i18n-title>
- Bucket Versioning can't be disabled when Object Locking is enabled.
- </cd-alert-panel>
- <cd-alert-panel type="warning"
- *ngIf="bucketForm.getValue('lock_enabled')">
- Enabling Object Locking will allow the configuration of GOVERNANCE or COMPLIANCE modes, which will help
- ensure that an object version cannot be overwritten or deleted for the specified period.
- </cd-alert-panel>
- </div>
- </div>
- </fieldset>
+ </cd-help-text>
+ </legend>
+ <!-- Object Locking enable -->
+ <cds-checkbox name="lock_enabled"
+ formControlName="lock_enabled"
+ id="lock_enabled"
+ [disabled]="editing"
+ i18n>Enable
- <!-- Encryption -->
- <fieldset>
- <legend class="cd-header"
- i18n>Encryption</legend>
- <div class="form-group row">
- <label class="cd-col-form-label pt-0"
- for="encryption_enabled"
- i18n>Enable
- </label>
- <div class="cd-col-form-input">
- <input class="form-check-input"
- id="encryption_enabled"
- name="encryption_enabled"
- formControlName="encryption_enabled"
- type="checkbox"
- [attr.disabled]="!kmsConfigured && !s3Configured ? true : null" />
- <cd-help-text aria-label="encryption helper">
- <span i18n>Enables encryption for the objects in the bucket.
- To enable encryption on a bucket you need to set the configuration values for SSE-S3 or SSE-KMS.
- To set the configuration values
- <a href="#/rgw/configuration"
- aria-label="click here">Click here</a></span>
- </cd-help-text>
- </div>
- </div>
- <div *ngIf="bucketForm.getValue('encryption_enabled')">
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-radio custom-control-inline ps-5">
- <input class="form-check-input"
- formControlName="encryption_type"
- id="sse_S3_enabled"
- type="radio"
- name="encryption_type"
- value="AES256"
- [attr.disabled]="!s3Configured ? true : null">
- <label class="form-control-label"
- [ngClass]="{'text-muted': !s3Configured}"
- for="sse_S3_enabled"
- i18n>SSE-S3</label>
- </div>
- </div>
- </div>
+ <cd-help-text>
+ <span>Enables locking for the objects in the bucket. Locking can only be enabled while creating a bucket.</span>
+ </cd-help-text>
+ </cds-checkbox>
+ </div>
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-radio custom-control-inline ps-5">
- <input class="form-check-input"
- formControlName="encryption_type"
- id="kms_enabled"
- name="encryption_type"
- value="aws:kms"
- [attr.disabled]="!kmsConfigured ? true : null"
- type="radio">
- <label class="form-control-label"
- [ngClass]="{'text-muted': !kmsConfigured}"
- for="kms_enabled"
- i18n>Connect to an external key management service</label>
- </div>
- </div>
- </div>
+ <!-- Object Locking mode -->
+ <div *ngIf="bucketForm.getValue('lock_enabled')"
+ class="form-item">
+ <cds-select label="Mode"
+ for="lock_mode"
+ formControlName="lock_mode"
+ name="lock_mode"
+ [helperText]="lockModeHelper"
+ id="lock_mode"
+ i18n>
+ <option value="COMPLIANCE">Compliance</option>
+ <option value="GOVERNANCE">Governance</option>
+ </cds-select>
+ <ng-template #lockModeHelper>
+ <span *ngIf="bucketForm.getValue('lock_mode') === 'COMPLIANCE'"
+ i18n>
+ In COMPLIANCE an object version cannot be overwritten or deleted for the duration of the period.
+ </span>
+ <span *ngIf="bucketForm.getValue('lock_mode') === 'GOVERNANCE'"
+ i18n>
+ In GOVERNANCE mode, users cannot overwrite or delete an object version or alter its lock settings unless they have special permissions.
+ </span>
+ </ng-template>
+ </div>
+ <!-- Retention period (days) -->
+ <div *ngIf="bucketForm.getValue('lock_enabled')"
+ class="form-item">
+ <cds-number name="lock_retention_period_days"
+ formControlName="lock_retention_period_days"
+ id="lock_retention_period_days"
+ min="1"
+ label="Retention period (days)"
+ helperText="The number of days that you want to specify for the default retention period that will be applied to new objects placed in this bucket."
+ [invalid]="bucketForm.controls.lock_retention_period_days.invalid && (bucketForm.controls.lock_retention_period_days.dirty)"
+ [invalidText]="retentionPeriodError"
+ i18n-helperText
+ i18n-label
+ i18n></cds-number>
+ <ng-template #retentionPeriodError>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('lock_retention_period_days', frm, 'pattern')"
+ i18n>The entered value must be a positive integer.</span>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('lock_retention_period_days', frm, 'lockDays')"
+ i18n>Retention Days must be a positive integer.</span>
+ </ng-template>
+ </div>
+ <!-- Alerts -->
+ <cd-alert-panel
+ type="info"
+ *ngIf="bucketForm.getValue('lock_enabled')"
+ class="me-1"
+ i18n-title>
+ Bucket Versioning can't be disabled when Object Locking is enabled.
+ </cd-alert-panel>
+ <cd-alert-panel
+ type="warning"
+ *ngIf="bucketForm.getValue('lock_enabled')">
+ Enabling Object Locking will allow the configuration of GOVERNANCE or COMPLIANCE modes, which will help ensure that an object version cannot be overwritten or deleted for the specified period.
+ </cd-alert-panel>
+ </fieldset>
- <div *ngIf="bucketForm.getValue('encryption_type') === 'aws:kms'">
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="kms_provider"
- i18n>KMS Provider</label>
- <div class="cd-col-form-input">
- <select id="kms_provider"
- name="kms_provider"
- class="form-select"
- formControlName="kms_provider"
- [autofocus]="editing">
- <option i18n
- *ngIf="kmsProviders !== null"
- [ngValue]="null">-- Select a provider --</option>
- <option *ngFor="let provider of kmsProviders"
- [value]="provider">{{ provider }}</option>
- </select>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('kms_provider', frm, 'required')"
- i18n>This field is required.</span>
- </div>
- </div>
- </div>
+ <!-- Encryption -->
+ <fieldset>
+ <div class="form-item">
+ <legend class="cd-header"
+ i18n>Encryption</legend>
+ <cds-checkbox name="encryption_enabled"
+ formControlName="encryption_enabled"
+ id="encryption_enabled"
+ [disabled]="!kmsConfigured && !s3Configured"
+ i18n>Enable
+ <cd-help-text aria-label="encryption helper">
+ <span>Enables encryption for the objects in the bucket.
+ To enable encryption on a bucket you need to set the configuration values for SSE-S3 or SSE-KMS.
+ To set the configuration values <a href="#/rgw/configuration"
+ i18n-aria-label="click here">Click here</a></span>
+ </cd-help-text>
+ </cds-checkbox>
+ </div>
- <div *ngIf="bucketForm.getValue('encryption_type') === 'aws:kms'">
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="keyId"
- i18n>Key Id
- </label>
- <div class="cd-col-form-input">
- <input id="keyId"
- name="keyId"
- class="form-control"
- type="text"
- formControlName="keyId">
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('keyId', frm, 'required')"
- i18n>This field is required.</span>
- </div>
- </div>
- </div>
- </div>
- </fieldset>
+ <div *ngIf="bucketForm.getValue('encryption_enabled')">
+ <div class="form-item">
+ <cds-radio-group formControlName="encryption_type">
+ <cds-radio value="AES256"
+ [disabled]="!s3Configured">SSE-S3</cds-radio>
+ <cds-radio value="aws:kms"
+ [disabled]="!kmsConfigured">Connect to an external key management service</cds-radio>
+ </cds-radio-group>
+ </div>
- <!-- Replication -->
- <fieldset>
- <legend class="cd-header"
- i18n>Replication</legend>
- <div class="form-group row">
- <label class="cd-col-form-label pt-0"
- for="replication"
- i18n>
- Enable
- </label>
- <div class="cd-col-form-input"
- *ngIf="{status: multisiteStatus$, isDefaultZg: isDefaultZoneGroup$ | async} as multisiteStatus; else loadingTpl">
- <input type="checkbox"
- class="form-check-input"
- id="replication"
- name="replication"
- formControlName="replication"
- [attr.disabled]="!multisiteStatus.isDefaultZg && !multisiteStatus.status.available ? true : null">
- <cd-help-text>
- <span i18n>Enables replication for the objects in the bucket.</span>
- </cd-help-text>
- <div class="mt-1"
- *ngIf="!editing">
- <cd-alert-panel type="info"
- class="me-1"
- id="replication-info"
- i18n>
- A bi-directional sync policy group will be created by the dashboard along with flows and pipes.
- The pipe id will then be used for applying the replication policy to the bucket.
- </cd-alert-panel>
- </div>
- </div>
+ <div *ngIf="bucketForm.getValue('encryption_type') === 'aws:kms'">
+ <div class="form-item">
+ <cds-select label="KMS Provider"
+ for="kms_provider"
+ formControlName="kms_provider"
+ name="kms_provider"
+ id="kms_provider"
+ [invalidText]="kmsProviderError"
+ [invalid]="!bucketForm.controls.kms_provider.valid && (bucketForm.controls.kms_provider.dirty)"
+ cdRequiredField="KMS Provider"
+ i18n>KMS Provider
+ <option *ngIf="kmsProviders === null"
+ [ngValue]="null">Loading...</option>
+ <option *ngIf="kmsProviders !== null"
+ [ngValue]="null">-- Select a provider --</option>
+ <option *ngFor="let provider of kmsProviders"
+ [value]="provider">{{ provider }}</option>
+ </cds-select>
+ <ng-template #kmsProviderError>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('kms_provider', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
</div>
- </fieldset>
- <!-- Tags -->
- <fieldset>
- <legend class="cd-header"
- i18n>Tags
- <cd-help-text>Tagging provides a way to categorize storage</cd-help-text>
- </legend>
- <span *ngFor="let tag of tags; let i=index;">
- <ng-container *ngTemplateOutlet="tagTpl; context:{index: i, tag: tag}"></ng-container>
- </span>
-
- <div class="row">
- <div class="col-12">
- <strong *ngIf="tags.length > 19"
- class="text-warning"
- i18n>Maximum of 20 tags reached</strong>
- <button type="button"
- id="add-tag"
- class="btn btn-light float-end my-3"
- [disabled]="tags.length > 19"
- (click)="showTagModal()">
- <i [ngClass]="[icons.add]"></i>
- <ng-container i18n>Add tag</ng-container>
- </button>
- </div>
+ <div class="form-item">
+ <cds-text-label for="keyId"
+ cdRequiredField="Key Id"
+ [invalid]="!bucketForm.controls.keyId.valid && (bucketForm.controls.keyId.dirty)"
+ [invalidText]="keyIdError"
+ i18n>Key Id
+ <input cdsText
+ id="keyId"
+ name="keyId"
+ formControlName="keyId"
+ [invalid]="!bucketForm.controls.keyId.valid && (bucketForm.controls.keyId.dirty)">
+ </cds-text-label>
+ <ng-template #keyIdError>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('keyId', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
</div>
- </fieldset>
+ </div>
+ </div>
+ </fieldset>
- <!-- Policies -->
- <fieldset>
- <legend class="cd-header"
- i18n>Policies
- </legend>
- <div class="row">
- <div class="col-12">
- <div class="form-group row">
+ <!-- Replication -->
+ <fieldset>
+ <div class="form-item">
+ <legend class="cd-header"
+ i18n>Replication</legend>
+ <ng-container *ngIf="{status: multisiteStatus$, isDefaultZg: isDefaultZoneGroup$ | async} as multisiteStatus; else loadingTpl">
+ <cds-checkbox name="replication"
+ formControlName="replication"
+ id="replication"
+ [disabled]="!multisiteStatus.isDefaultZg && !multisiteStatus.status.available"
+ i18n-helperText
+ i18n>Enable
+ <cd-help-text>Enables replication for the objects in the bucket.</cd-help-text>
+ </cds-checkbox>
- <!-- Bucket policy -->
- <label i18n
- class="cd-col-form-label"
- for="id">Bucket policy</label>
- <div class="cd-col-form-input">
- <textarea #bucketPolicyTextArea
- class="form-control resize-vertical"
- id="bucket_policy"
- formControlName="bucket_policy"
- (change)="textAreaOnChange('bucketPolicyTextArea')">
- </textarea>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('bucket_policy', frm, 'invalidJson')"
- i18n>Invalid json text.</span>
- <button type="button"
- id="clear-bucket-policy"
- class="btn btn-light my-3"
- (click)="clearTextArea('bucket_policy', '{}')"
+ <cd-alert-panel type="info"
+ class="me-1"
+ id="replication-info"
+ spacingClass="mt-1"
+ *ngIf="!editing"
i18n>
- <i [ngClass]="[icons.destroy]"></i>
- Clear
- </button>
- <div class="btn-group float-end"
- role="group"
- aria-label="bucket-policy-helpers">
- <button type="button"
- id="example-generator-button"
- class="btn btn-light my-3"
- (click)="openUrl('https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html?icmpid=docs_amazons3_console')"
- i18n>
- <i [ngClass]="[icons.externalUrl]"></i>
- Policy examples
- </button>
- <button type="button"
- id="example-generator-button"
- class="btn btn-light my-3"
- (click)="openUrl('https://awspolicygen.s3.amazonaws.com/policygen.html')"
- i18n>
- <i [ngClass]="[icons.externalUrl]"></i>
- Policy generator
- </button>
- </div>
- </div>
- </div>
+ A bi-directional sync policy group will be created by the dashboard along with flows and pipes.
+ The pipe id will then be used for applying the replication policy to the bucket.
+ </cd-alert-panel>
+ </ng-container>
+ </div>
+ </fieldset>
- <!-- Lifecycle -->
- <div *ngIf="editing"
- class="form-group row">
- <label i18n
- class="cd-col-form-label"
- for="id">Lifecycle
- <cd-helper>JSON or XML formatted document</cd-helper>
- </label>
- <div class="cd-col-form-input">
- <textarea #lifecycleTextArea
- class="form-control resize-vertical"
- id="lifecycle"
- formControlName="lifecycle"
- (change)="textAreaOnChange('lifecycleTextArea')">
- </textarea>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('lifecycle', frm, 'invalidJson')"
- i18n>Invalid json text.</span>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('lifecycle', frm, 'invalidXml')"
- i18n>Invalid xml text.</span>
- <button type="button"
- id="clear-lifecycle"
- class="btn btn-light my-3"
- (click)="clearTextArea('lifecycle', '{}')"
- i18n>
- <i [ngClass]="[icons.destroy]"></i>
- Clear
- </button>
- <div class="btn-group float-end"
- role="group"
- aria-label="bucket-policy-helpers">
- <button type="button"
- id="lifecycle-examples-button"
- class="btn btn-light my-3"
- (click)="openUrl('https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-lifecycle.html#examples')"
- i18n>
- <i [ngClass]="[icons.externalUrl]"></i>
- Policy examples
- </button>
- </div>
- </div>
- </div>
+ <!-- Tags -->
+ <fieldset>
+ <legend class="cd-header"
+ i18n>Tags
+ <cd-help-text>Tagging provides a way to categorize storage</cd-help-text>
+ </legend>
+ <span *ngFor="let tag of tags; let i=index;">
+ <ng-container *ngTemplateOutlet="tagTpl; context:{index: i, tag: tag}"></ng-container>
+ </span>
- <div class="form-group row">
+ <div cdsRow>
+ <div cdsCol>
+ <cds-tooltip [description]="tags.length > 19 ? 'Maximum of 20 tags reached' : ''"
+ [highContrast]="true"
+ [caret]="true">
+ <button cdsButton="tertiary"
+ id="add-tag"
+ [disabled]="tags.length > 19"
+ (click)="showTagModal()">
+ <ng-container i18n>Add tag</ng-container>
+ <svg cdsIcon="add"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </button>
+ </cds-tooltip>
+ </div>
+ </div>
+ </fieldset>
- <!-- ACL -->
- <label class="cd-col-form-label"
- i18n>ACL
- <cd-helper>Any changes to the ACL will overwrite previous one.
- You can choose any of the available options to modify the spcified user group.</cd-helper>
- </label>
- <div class="cd-col-form-input">
- <div class="input-group">
- <span class="input-group-text"
- for="grantee"
- i18n>Grantee
- <cd-helper>Select a grantee (user group) to modify it's permisions</cd-helper>
- </span>
- <select id="grantee"
- name="grantee"
- class="form-input form-select"
- formControlName="grantee"
- (change)="onSelectionFilter()">
- <option *ngFor="let item of grantees"
- [value]="item"
- i18n>{{ item }}</option>
- </select>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('grantee', frm, 'required')"
- i18n>This field is required.</span>
- <span class="input-group-text"
- for="aclPermission"
- i18n>Permissions
- <cd-helper>Select the permision to give to the selected grantee.
- Regardless, the owner of the bucket will always have
- FULL CONTROL access</cd-helper>
- </span>
- <select id="aclPermission"
- name="aclPermission"
- class="form-input form-select"
- formControlName="aclPermission">
- <option *ngFor="let permission of aclPermissions"
- [value]="permission"
- i18n>{{ permission }}
- </option>
- </select>
- <span class="invalid-feedback"
- *ngIf="bucketForm.showError('aclPermission', frm, 'required')"
- i18n>This field is required.</span>
- </div>
- </div>
- </div>
- </div>
+ <!-- Policies -->
+ <fieldset>
+ <div class="form-item">
+ <legend class="cd-header"
+ i18n>Policies
+ </legend>
+
+ <!-- Bucket policy -->
+ <cds-textarea-label for="id"
+ [invalid]="!bucketForm.controls.bucket_policy.valid && (bucketForm.controls.bucket_policy.dirty)"
+ [invalidText]="bucketPolicyError"
+ i18n>Bucket Policy
+ <textarea cdsTextArea
+ class="textarea-field"
+ id="bucket_policy"
+ formControlName="bucket_policy"
+ (change)="textAreaOnChange('bucket_policy')"
+ cols="200"
+ #bucketPolicyTextArea></textarea>
+ </cds-textarea-label>
+ <ng-template #bucketPolicyError>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('bucket_policy', frm, 'invalidJson')"
+ i18n>Invalid json text.</span>
+ </ng-template>
+ <div cdsRow>
+ <div cdsCol>
+ <cds-button-set class="mt-1">
+ <button cdsButton="tertiary"
+ id="example-generator-button"
+ (click)="openUrl('https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html?icmpid=docs_amazons3_console')"
+ i18n>Policy examples
+ <svg cdsIcon="launch"
+ size="16"
+ class="cds--btn__icon"></svg>
+ </button>
+ <button cdsButton="tertiary"
+ id="example-generator-button"
+ (click)="openUrl('https://awspolicygen.s3.amazonaws.com/policygen.html')"
+ i18n>Policy generator
+ <svg cdsIcon="launch"
+ size="16"
+ class="cds--btn__icon"></svg>
+ </button>
+ </cds-button-set>
+ </div>
+ <div cdsCol>
+ <cds-button-set class="float-end mt-1">
+ <button cdsButton="tertiary"
+ id="clear-bucket-policy"
+ (click)="clearTextArea('bucket_policy', '{}')"
+ i18n>Clear
+ <svg cdsIcon="close"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </button>
+ </cds-button-set>
</div>
- </fieldset>
+ </div>
+ </div>
- <!--Advanced-->
- <cd-form-advanced-fieldset>
- <!-- Placement target -->
- <div class="form-group row"
- *ngIf="!editing">
- <label class="cd-col-form-label"
- for="placement-target"
- i18n>Placement target</label>
- <div class="cd-col-form-input">
- <select id="placement-target"
- name="placement-target"
- formControlName="placement-target"
- class="form-select">
- <option i18n
- *ngIf="placementTargets === null"
- [ngValue]="null">Loading...</option>
- <option i18n
- *ngIf="placementTargets !== null"
- [ngValue]="null">-- Select a placement target --</option>
- <option *ngFor="let placementTarget of placementTargets"
- [value]="placementTarget.name">{{placementTarget.description }}</option>
- </select>
- <cd-help-text>
- <span i18n>
- When creating a bucket, a placement target can be provided as part of the LocationConstraint to override the default placement targets from the user and zonegroup.
- </span>
- </cd-help-text>
- </div>
+ <!-- Lifecycle -->
+ <div *ngIf="editing"
+ class="form-item">
+ <cds-textarea-label for="id"
+ [invalid]="!bucketForm.controls.lifecycle.valid && bucketForm.controls.lifecycle.dirty"
+ [invalidText]="lifecycleError"
+ i18n>Lifecycle
+ <textarea cdsTextArea
+ class="textarea-field"
+ id="lifecycle"
+ formControlName="lifecycle"
+ (change)="textAreaOnChange('lifecycle')"
+ cols="200"
+ #lifecycleTextArea></textarea>
+ <cd-help-text>JSON or XML formatted document</cd-help-text>
+ </cds-textarea-label>
+ <ng-template #lifecycleError>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('lifecycle', frm, 'invalidJson')"
+ i18n>Invalid json text.</span>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('lifecycle', frm, 'invalidXml')"
+ i18n>Invalid xml text.</span>
+ </ng-template>
+ <div cdsRow>
+ <div cdsCol>
+ <cds-button-set class="mt-1">
+ <button cdsButton="tertiary"
+ id="lifecycle-examples-button"
+ (click)="openUrl('https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-lifecycle.html#examples')"
+ i18n>Policy examples
+ <svg cdsIcon="launch"
+ size="16"
+ class="cds--btn__icon"></svg>
+ </button>
+ </cds-button-set>
</div>
- <!-- Bucket Rate Limit -->
+ <div cdsCol>
+ <cds-button-set class="float-end mt-1">
+ <button cdsButton="tertiary"
+ id="clear-lifecycle"
+ (click)="clearTextArea('lifecycle', '{}')"
+ i18n>Clear
+ <svg cdsIcon="close"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </button>
+ </cds-button-set>
+ </div>
+ </div>
+ </div>
+
+ <div class="form-item">
- <cd-rgw-rate-limit [type]="'bucket'"
- [isEditing]="this.editing"
- [allowBid]="this.bucketForm.getValue('bid')"
- (rateLimitFormGroup)="rateLimitFormInit($event)"></cd-rgw-rate-limit>
- </cd-form-advanced-fieldset>
+ <!-- ACL -->
+ <cds-text-label i18n>ACL
+ <cd-help-text>Any changes to the ACL will overwrite previous one.
+ You can choose any of the available options to modify the spcified user group.</cd-help-text>
+ </cds-text-label>
+ <div cdsRow
+ class="form-item-append">
+ <div cdsCol>
+ <cds-select id="grantee"
+ name="grantee"
+ label="Grantee"
+ formControlName="grantee"
+ helperText="Select a grantee (user group) to modify it's permisions"
+ [invalid]="!bucketForm.controls.grantee.valid && (bucketForm.controls.grantee.dirty)"
+ [invalidText]="granteeError"
+ (valueChange)="onSelectionFilter()"
+ i18n>Grantee
+ <option *ngFor="let item of grantees"
+ [value]="item">{{ item }}</option>
+ </cds-select>
+ <ng-template #granteeError>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('grantee', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
+ </div>
+ <div cdsCol>
+ <cds-select id="aclPermission"
+ name="aclPermission"
+ [invalid]="!bucketForm.controls.aclPermission.valid && (bucketForm.controls.aclPermission.dirty)"
+ [invalidText]="aclPermissionError"
+ label="Permissions"
+ helperText="Select the permision to give to the selected grantee. Regardless, the owner of the bucket will always have FULL CONTROL access"
+ formControlName="aclPermission">
+ <option *ngFor="let permission of aclPermissions"
+ [value]="permission"
+ i18n>{{ permission }}</option>
+ </cds-select>
+ <ng-template #aclPermissionError>
+ <span class="invalid-feedback"
+ *ngIf="bucketForm.showError('aclPermission', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
+ </div>
+ </div>
</div>
+ </fieldset>
- <div class="card-footer">
- <cd-form-button-panel (submitActionEvent)="submit()"
- [form]="bucketForm"
- [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"
- wrappingClass="text-right"></cd-form-button-panel>
+ <!--Advanced-->
+ <cd-form-advanced-fieldset>
+ <!-- Placement target -->
+ <div class="form-item"
+ *ngIf="!editing">
+ <cds-select label="Placement target"
+ for="placement-target"
+ formControlName="placement-target"
+ name="placement-target"
+ id="placement-target"
+ helperText="When creating a bucket, a placement target can be provided as part of the LocationConstraint to override the default placement targets from the user and zonegroup."
+ i18n-helperText
+ i18n>Placement target
+ <option *ngIf="placementTargets === null"
+ [ngValue]="null">Loading...</option>
+ <option *ngIf="placementTargets !== null"
+ [ngValue]="null">-- Select a placement target --</option>
+ <option *ngFor="let placementTarget of placementTargets"
+ [value]="placementTarget.name">{{ placementTarget.description }}</option>
+ </cds-select>
</div>
- </div>
+
+ <!-- Bucket Rate Limit -->
+ <cd-rgw-rate-limit [type]="'bucket'"
+ [isEditing]="this.editing"
+ [allowBid]="this.bucketForm.getValue('bid')"
+ (rateLimitFormGroup)="rateLimitFormInit($event)">
+ </cd-rgw-rate-limit>
+ </cd-form-advanced-fieldset>
+
+ <cd-form-button-panel (submitActionEvent)="submit()"
+ [form]="bucketForm"
+ [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"
+ wrappingClass="text-right"></cd-form-button-panel>
</form>
</div>
<ng-template #tagTpl
let-tag="tag"
let-index="index">
- <div class="input-group my-2">
+ <div class="form-item form-item-append">
<ng-container *ngFor="let config of tagConfig">
<input type="text"
id="tag-{{config.attribute}}-{{index}}"
- class="form-control"
+ cdsText
[ngbTooltip]="config.attribute"
[value]="tag[config.attribute]"
- disabled
readonly>
</ng-container>
<!-- Tag actions -->
- <button type="button"
- class="btn btn-light"
- id="tag-edit-{{index}}"
- i18n-ngbTooltip
- ngbTooltip="Edit"
- (click)="showTagModal(index)">
- <i [ngClass]="[icons.edit]"></i>
- </button>
- <button type="button"
- class="btn btn-light"
- id="tag-delete-{{index}}"
- i18n-ngbTooltip
- ngbTooltip="Delete"
- (click)="deleteTag(index)">
- <i [ngClass]="[icons.trash]"></i>
- </button>
+ <cds-icon-button kind="tertiary"
+ size="md"
+ (click)="showTagModal(index)">
+ <svg cdsIcon="edit"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </cds-icon-button>
+ <cds-icon-button kind="danger"
+ size="md"
+ (click)="deleteTag(index)">
+ <svg cdsIcon="trash-can"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </cds-icon-button>
</div>
</ng-template>
<ng-template #loadingTpl>
- <div class="cd-col-form-input">
- <cd-loading-panel i18n>Checking multi-site status...</cd-loading-panel>
- </div>
+ <cd-loading-panel i18n>Checking multi-site status...</cd-loading-panel>
</ng-template>
import { RgwBucketVersioning } from '../models/rgw-bucket-versioning';
import { RgwBucketFormComponent } from './rgw-bucket-form.component';
import { RgwRateLimitComponent } from '../rgw-rate-limit/rgw-rate-limit.component';
+import { CheckboxModule, SelectModule } from 'carbon-components-angular';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
describe('RgwBucketFormComponent', () => {
let component: RgwBucketFormComponent;
ReactiveFormsModule,
RouterTestingModule,
SharedModule,
- ToastrModule.forRoot()
- ]
+ ToastrModule.forRoot(),
+ SelectModule,
+ CheckboxModule
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
});
beforeEach(() => {
import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder';
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { CdValidators } from '~/app/shared/forms/cd-validators';
-import { ModalService } from '~/app/shared/services/modal.service';
import { NotificationService } from '~/app/shared/services/notification.service';
import { rgwBucketEncryptionModel } from '../models/rgw-bucket-encryption';
import { RgwBucketMfaDelete } from '../models/rgw-bucket-mfa-delete';
import { TextAreaXmlFormatterService } from '~/app/shared/services/text-area-xml-formatter.service';
import { RgwRateLimitComponent } from '../rgw-rate-limit/rgw-rate-limit.component';
import { RgwRateLimitConfig } from '../models/rgw-rate-limit';
+import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
@Component({
selector: 'cd-rgw-bucket-form',
private formBuilder: CdFormBuilder,
private rgwBucketService: RgwBucketService,
private rgwSiteService: RgwSiteService,
- private modalService: ModalService,
+ private modalService: ModalCdsService,
private rgwUserService: RgwUserService,
private notificationService: NotificationService,
private textAreaJsonFormatterService: TextAreaJsonFormatterService,
showTagModal(index?: number) {
const modalRef = this.modalService.show(BucketTagModalComponent);
- const modalComponent = modalRef.componentInstance as BucketTagModalComponent;
+ const modalComponent = modalRef as BucketTagModalComponent;
modalComponent.currentKeyTags = this.tags.map((item) => item.key);
if (_.isNumber(index)) {
[open]="open"
(overlaySelected)="closeModal()">
<cds-modal-header (closeSelect)="closeModal()"
- i18n>{{editing ? 'Edit' : 'Create'}} Tiering configuration</cds-modal-header>
+ i18n>{{editing ? 'Edit' : 'Create'}} Tiering configuration
+ <cd-help-text [formAllFieldsRequired]="true"></cd-help-text>
+ </cds-modal-header>
<ng-container *cdFormLoading="loading">
<section cdsModalContent>
- <legend>
- <cd-help-text i18n>
- All fields are required, except where marked optional.
- </cd-help-text>
- </legend>
<cd-alert-panel
*ngIf="(snapScheduleModuleStatus$ | async) === false"
type="info"
<fieldset *ngIf="type">
- <legend i18n>
+ <legend class="cd-header"
+ i18n>
Rate Limit
<cd-help-text *ngIf="type === 'user'">
The User Rate Limit controls the max read/write operations and data per minute for each user.
</div>
</div>
</div>
+
<!-- Enabled -->
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input
- class="custom-control-input"
- id="rate_limit_enabled"
- type="checkbox"
- formControlName="rate_limit_enabled"
- />
- <label class="custom-control-label"
- for="rate_limit_enabled"
- i18n>Enabled</label>
- <cd-help-text i18n>Toggle to enable or disable the rate limit settings.</cd-help-text>
- </div>
- </div>
+ <div class="form-item">
+ <cds-checkbox id="rate_limit_enabled"
+ formControlName="rate_limit_enabled">
+ <ng-container i18n>Enabled</ng-container>
+ <cd-help-text i18n>Toggle to enable or disable the rate limit settings.</cd-help-text>
+ </cds-checkbox>
</div>
- <!-- Unlimited size -->
- <div class="form-group row"
+
+ <!-- Unlimited read ops -->
+ <div class="form-item"
*ngIf="form.controls.rate_limit_enabled.value">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input
- class="custom-control-input"
- id="rate_limit_max_readOps_unlimited"
- type="checkbox"
- formControlName="rate_limit_max_readOps_unlimited"
- />
- <label class="custom-control-label"
- for="rate_limit_max_readOps_unlimited"
- i18n>Unlimited read ops
- </label>
- <cd-help-text i18n>Select this box to allow unlimited read operations.</cd-help-text>
- </div>
- </div>
+ <cds-checkbox id="rate_limit_max_readOps_unlimited"
+ formControlName="rate_limit_max_readOps_unlimited">
+ <ng-container i18n>Unlimited read ops</ng-container>
+ <cd-help-text i18n>Select this box to allow unlimited read operations.</cd-help-text>
+ </cds-checkbox>
</div>
- <!-- Maximum size -->
+ <!-- Maximum read ops -->
<div
- class="form-group row"
+ class="form-item"
*ngIf="
form.controls.rate_limit_enabled.value && !form.getValue('rate_limit_max_readOps_unlimited')
"
>
- <label class="cd-col-form-label required"
- for="rate_limit_max_readOps"
- i18n>Max. read ops</label>
- <div class="cd-col-form-input">
- <input
- id="rate_limit_max_readOps"
- class="form-control"
- type="number"
- formControlName="rate_limit_max_readOps"
- />
- <cd-help-text>Limits the number of read operations per minute for a user.</cd-help-text>
+ <cds-number id="rate_limit_max_readOps"
+ formControlName="rate_limit_max_readOps"
+ label="Maximum read ops"
+ i18n-label
+ cdRequiredField="Maximum read ops"
+ helperText="Limits the number of read operations per minute for a user."
+ i18n-helperText
+ [invalid]="form.controls.rate_limit_max_readOps.invalid && form.controls.rate_limit_max_readOps.dirty"
+ [invalidText]="rateLimitMaxReadOpsError">
+ </cds-number>
+
+ <ng-template #rateLimitMaxReadOpsError>
<span
class="invalid-feedback"
*ngIf="form.showError('rate_limit_max_readOps', frm, 'required')"
i18n
>Enter a positive number.</span
>
- </div>
+ </ng-template>
</div>
+
<!-- Unlimited Write Ops -->
- <div class="form-group row"
+ <div class="form-item"
*ngIf="form.controls.rate_limit_enabled.value">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input
- class="custom-control-input"
- id="rate_limit_max_writeOps_unlimited"
- type="checkbox"
- formControlName="rate_limit_max_writeOps_unlimited"
- />
- <label class="custom-control-label"
- for="rate_limit_max_writeOps_unlimited"
- i18n>Unlimited write ops
- </label>
- <cd-help-text i18n>Select this box to allow unlimited write operations.</cd-help-text>
- </div>
- </div>
+ <cds-checkbox id="rate_limit_max_writeOps_unlimited"
+ formControlName="rate_limit_max_writeOps_unlimited">
+ <ng-container i18n>Unlimited write ops</ng-container>
+ <cd-help-text i18n>Select this box to allow unlimited write operations.</cd-help-text>
+ </cds-checkbox>
</div>
+
<!-- Maximum Write Ops -->
<div
- class="form-group row"
+ class="form-item"
*ngIf="
form.controls.rate_limit_enabled.value &&
!form.getValue('rate_limit_max_writeOps_unlimited')
"
>
- <label class="cd-col-form-label required"
- for="rate_limit_max_writeOps"
- i18n>Max. write ops</label>
- <div class="cd-col-form-input">
- <input
- id="rate_limit_max_writeOps"
- class="form-control"
- type="number"
- formControlName="rate_limit_max_writeOps"
- />
- <cd-help-text>Limits the number of write operations per minute for a user.</cd-help-text>
+ <cds-number id="rate_limit_max_writeOps"
+ formControlName="rate_limit_max_writeOps"
+ label="Maximum write ops"
+ i18n-label
+ cdRequiredField="Maximum write ops"
+ helperText="Limits the number of write operations per minute for a user."
+ i18n-helperText
+ [invalid]="form.controls.rate_limit_max_writeOps.invalid && form.controls.rate_limit_max_writeOps.dirty"
+ [invalidText]="rateLimitMaxWriteOpsError">
+ </cds-number>
+
+ <ng-template #rateLimitMaxWriteOpsError>
<span
class="invalid-feedback"
*ngIf="form.showError('rate_limit_max_writeOps', frm, 'required')"
i18n
>Enter a positive number.</span
>
- </div>
+ </ng-template>
</div>
+
<!-- Unlimited Read Bytes -->
- <div class="form-group row"
+ <div class="form-item"
*ngIf="form.controls.rate_limit_enabled.value">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input
- class="custom-control-input"
- id="rate_limit_max_readBytes_unlimited"
- type="checkbox"
- formControlName="rate_limit_max_readBytes_unlimited"
- />
- <label class="custom-control-label"
- for="rate_limit_max_readBytes_unlimited"
- i18n>Unlimited read bytes
- </label>
- <cd-help-text i18n>Select this box to allow unlimited read bytes.</cd-help-text>
- </div>
- </div>
+ <cds-checkbox id="rate_limit_max_readBytes_unlimited"
+ formControlName="rate_limit_max_readBytes_unlimited">
+ <ng-container i18n>Unlimited read bytes</ng-container>
+ <cd-help-text i18n>Select this box to allow unlimited read bytes.</cd-help-text>
+ </cds-checkbox>
</div>
+
<!-- Maximum Read Bytes -->
<div
- class="form-group row"
+ class="form-item"
*ngIf="
form.controls.rate_limit_enabled.value &&
!form.getValue('rate_limit_max_readBytes_unlimited')
"
>
- <label class="cd-col-form-label required"
- for="rate_limit_max_readBytes"
- i18n>Max. read bytes</label
- >
- <div class="cd-col-form-input">
- <input
- id="rate_limit_max_readBytes"
- class="form-control"
- type="text"
- defaultUnit="b"
- formControlName="rate_limit_max_readBytes"
- cdDimlessBinaryPerMinute
- />
- <cd-help-text>Limits the number of read bytes per minute for a user.</cd-help-text>
+ <cds-text-label labelInputID="rate_limit_max_readBytes"
+ cdRequiredField="Maximum read bytes"
+ helperText="Limits the number of read bytes per minute for a user."
+ i18n-helperText
+ [invalid]="form.controls.rate_limit_max_readBytes.invalid && form.controls.rate_limit_max_readBytes.dirty"
+ [invalidText]="maxReadBytesError">
+ <input cdsText
+ id="rate_limit_max_readBytes"
+ formControlName="rate_limit_max_readBytes"
+ defaultUnit="b"
+ [invalid]="form.controls.rate_limit_max_readBytes.invalid && form.controls.rate_limit_max_readBytes.dirty"
+ cdDimlessBinaryPerMinute>
+ </cds-text-label>
+
+ <ng-template #maxReadBytesError>
<span
class="invalid-feedback"
*ngIf="form.showError('rate_limit_max_readBytes', frm, 'required')"
class="invalid-feedback"
*ngIf="form.showError('rate_limit_max_readBytes', frm, 'min')"
i18n>Enter a positive number.</span>
- </div>
+ </ng-template>
</div>
+
<!-- Unlimited Write Bytes -->
- <div class="form-group row"
+ <div class="form-item"
*ngIf="form.controls.rate_limit_enabled.value">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input
- class="custom-control-input"
- id="rate_limit_max_writeBytes_unlimited"
- type="checkbox"
- formControlName="rate_limit_max_writeBytes_unlimited"
- />
- <label class="custom-control-label"
- for="rate_limit_max_writeBytes_unlimited"
- i18n>Unlimited write bytes
- </label>
- <cd-help-text i18n>Select this box to allow unlimited write bytes.</cd-help-text>
- </div>
- </div>
+ <cds-checkbox id="rate_limit_max_writeBytes_unlimited"
+ formControlName="rate_limit_max_writeBytes_unlimited">
+ <ng-container i18n>Unlimited write bytes</ng-container>
+ <cd-help-text i18n>Select this box to allow unlimited write bytes.</cd-help-text>
+ </cds-checkbox>
</div>
+
<!-- Maximum Write Bytes -->
<div
- class="form-group row"
+ class="form-item"
*ngIf="
form.controls.rate_limit_enabled.value &&
!form.getValue('rate_limit_max_writeBytes_unlimited')
">
- <label class="cd-col-form-label required"
- for="rate_limit_max_writeBytes"
- i18n>Max. write bytes</label>
- <div class="cd-col-form-input">
- <input
- id="rate_limit_max_writeBytes"
- class="form-control"
- type="text"
- defaultUnit="b"
- formControlName="rate_limit_max_writeBytes"
- cdDimlessBinaryPerMinute
- />
- <cd-help-text>Limits the number of write bytes per minute for a user.</cd-help-text>
+ <cds-text-label labelInputID="rate_limit_max_writeBytes"
+ cdRequiredField="Maximum write bytes"
+ helperText="Limits the number of write bytes per minute for a user."
+ i18n-helperText
+ [invalid]="form.controls.rate_limit_max_writeBytes.invalid && form.controls.rate_limit_max_writeBytes.dirty"
+ [invalidText]="maxWriteBytesError">
+ <input cdsText
+ id="rate_limit_max_writeBytes"
+ formControlName="rate_limit_max_writeBytes"
+ defaultUnit="b"
+ [invalid]="form.controls.rate_limit_max_writeBytes.invalid && form.controls.rate_limit_max_writeBytes.dirty"
+ cdDimlessBinaryPerMinute>
+ </cds-text-label>
+
+ <ng-template #maxWriteBytesError>
<span
class="invalid-feedback"
- *ngIf="form.showError('rate_limit_max_writeBytes', frm, 'required')"
+ *ngIf="form.showError('rate_limit_max_readBytes', frm, 'required')"
i18n>This field is required.</span>
<span
class="invalid-feedback"
- *ngIf="form.showError('rate_limit_max_writeBytes', frm, 'rateByteMaxSize')"
+ *ngIf="form.showError('rate_limit_max_readBytes', frm, 'rateByteMaxSize')"
i18n>The value is not valid.</span>
<span
class="invalid-feedback"
- *ngIf="form.showError('rate_limit_max_writeBytes', frm, 'min')"
+ *ngIf="form.showError('rate_limit_max_readBytes', frm, 'min')"
i18n>Enter a positive number.</span>
- </div>
+ </ng-template>
</div>
</form>
</fieldset>
<div i18n="form title"
class="form-header">
{{ action | titlecase }} {{ resource | upperFirst }}
+
+ <cd-help-text [formAllFieldsRequired]="true"></cd-help-text>
</div>
- <legend>
- <cd-help-text i18n>
- All fields are required, except where marked optional.
- </cd-help-text>
- </legend>
<div class="form-item form-item-append"
cdsRow>
<div cdsCol>
-<cd-modal [modalRef]="activeModal">
- <ng-container i18n="form title"
- class="modal-title">{{ action | titlecase }} {{ resource | upperFirst }}</ng-container>
+<cds-modal size="md"
+ [open]="open"
+ [hasScrollingContent]="false"
+ (overlaySelected)="closeModal()">
+ <cds-modal-header (closeSelect)="closeModal()">
+ <h3 cdsModalHeaderHeading
+ i18n>{{ action | titlecase }} {{ resource | upperFirst }}</h3>
+
+ <cd-help-text [formAllFieldsRequired]="true"></cd-help-text>
+ </cds-modal-header>
- <ng-container class="modal-content">
<form #frm="ngForm"
[formGroup]="formGroup"
novalidate>
- <div class="modal-body">
+ <div cdsModalContent>
<!-- Type -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- [ngClass]="{'required': !editing}"
- for="type"
- i18n>Type</label>
- <div class="cd-col-form-input">
- <input id="type"
- class="form-control"
- type="text"
- *ngIf="editing"
+ <div class="form-item">
+ <cds-text-label for="type"
+ [invalid]="formGroup.controls.type.invalid && formGroup.controls.type.dirty"
+ [invalidText]="typeError"
+ *ngIf="editing"
+ i18n>Type
+ <input cdsText
+ id="type"
+ name="type"
+ formControlName="type"
[readonly]="true"
- formControlName="type">
- <select id="type"
- class="form-select"
- formControlName="type"
- *ngIf="!editing"
- autofocus>
- <option i18n
- *ngIf="types !== null"
- [ngValue]="null">-- Select a type --</option>
- <option *ngFor="let type of types"
- [value]="type">{{ type }}</option>
- </select>
+ modal-primary-focus>
+ </cds-text-label>
+
+ <cds-select *ngIf="!editing"
+ label="Type"
+ i18n-label
+ for="type"
+ formControlName="type"
+ [invalid]="formGroup.controls.type.invalid && formGroup.controls.type.dirty"
+ [invalidText]="typeError">
+ <option value="">--- Select a type ---</option>
+ <option *ngFor="let type of types"
+ [value]="type">{{ type }}</option>
+ </cds-select>
+ <ng-template #typeError>
<span class="invalid-feedback"
*ngIf="formGroup.showError('type', frm, 'required')"
i18n>This field is required.</span>
- </div>
+ </ng-template>
</div>
<!-- Permission -->
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="perm"
- i18n>Permission</label>
- <div class="cd-col-form-input">
- <select id="perm"
- class="form-select"
- formControlName="perm">
- <option i18n
- [ngValue]="null">-- Select a permission --</option>
- <option *ngFor="let perm of ['read', 'write', '*']"
- [value]="perm">
- {{ perm }}
- </option>
- </select>
+ <div class="form-item">
+ <cds-select label="Permission"
+ i18n-label
+ for="perm"
+ formControlName="perm"
+ [invalid]="formGroup.controls.perm.invalid && formGroup.controls.perm.dirty"
+ [invalidText]="permError">
+ <option value="">--- Select a permission ---</option>
+ <option *ngFor="let perm of ['read', 'write', '*']"
+ [value]="perm">
+ {{ perm }}
+ </option>
+ </cds-select>
+
+ <ng-template #permError>
<span class="invalid-feedback"
*ngIf="formGroup.showError('perm', frm, 'required')"
i18n>This field is required.</span>
- </div>
+ </ng-template>
</div>
</div>
- <div class="modal-footer">
- <cd-form-button-panel (submitActionEvent)="onSubmit()"
- [form]="formGroup"
- [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"></cd-form-button-panel>
- </div>
- </form>
- </ng-container>
-</cd-modal>
+ <cd-form-button-panel (submitActionEvent)="onSubmit()"
+ [form]="formGroup"
+ [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"
+ [modalForm]="true"></cd-form-button-panel>
+
+ </form>
+</cds-modal>
import { ReactiveFormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
-
import { SharedModule } from '~/app/shared/shared.module';
import { configureTestBed } from '~/testing/unit-test-helper';
import { RgwUserCapabilityModalComponent } from './rgw-user-capability-modal.component';
+import { SelectModule } from 'carbon-components-angular';
describe('RgwUserCapabilityModalComponent', () => {
let component: RgwUserCapabilityModalComponent;
configureTestBed({
declarations: [RgwUserCapabilityModalComponent],
- imports: [ReactiveFormsModule, SharedModule, RouterTestingModule],
- providers: [NgbActiveModal]
+ imports: [ReactiveFormsModule, SharedModule, RouterTestingModule, SelectModule]
});
beforeEach(() => {
import { Component, EventEmitter, Output } from '@angular/core';
import { Validators } from '@angular/forms';
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import _ from 'lodash';
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { RgwUserCapabilities } from '../models/rgw-user-capabilities';
import { RgwUserCapability } from '../models/rgw-user-capability';
+import { BaseModal } from 'carbon-components-angular';
@Component({
selector: 'cd-rgw-user-capability-modal',
templateUrl: './rgw-user-capability-modal.component.html',
styleUrls: ['./rgw-user-capability-modal.component.scss']
})
-export class RgwUserCapabilityModalComponent {
+export class RgwUserCapabilityModalComponent extends BaseModal {
/**
* The event that is triggered when the 'Add' or 'Update' button
* has been pressed.
resource: string;
action: string;
- constructor(
- private formBuilder: CdFormBuilder,
- public activeModal: NgbActiveModal,
- public actionLabels: ActionLabelsI18n
- ) {
+ constructor(private formBuilder: CdFormBuilder, public actionLabels: ActionLabelsI18n) {
+ super();
this.resource = $localize`capability`;
this.createForm();
}
onSubmit() {
const capability: RgwUserCapability = this.formGroup.value;
this.submitAction.emit(capability);
- this.activeModal.close();
+ this.closeModal();
}
}
-<div class="cd-col-form"
+<div cdsCol
+ [columnNumbers]="{md: 4}"
*cdFormLoading="loading">
<form #frm="ngForm"
[formGroup]="userForm"
novalidate>
- <div class="card">
- <div i18n="form title"
- class="card-header">{{ action | titlecase }} {{ resource | upperFirst }}</div>
-
- <div class="card-body">
- <!-- User ID -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- [ngClass]="{'required': !editing}"
- for="user_id"
- i18n>User ID</label>
- <div class="cd-col-form-input">
- <input id="user_id"
- class="form-control"
- type="text"
- formControlName="user_id"
- [readonly]="editing">
- <span class="invalid-feedback"
- *ngIf="userForm.showError('user_id', frm, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('user_id', frm, 'pattern')"
- i18n>The value is not valid.</span>
- <span class="invalid-feedback"
- *ngIf="!userForm.getValue('show_tenant') && userForm.showError('user_id', frm, 'notUnique')"
- i18n>The chosen user ID is already in use.</span>
- </div>
- </div>
+ <div i18n="form title"
+ class="form-header">{{ action | titlecase }} {{ resource | upperFirst }}</div>
+
+ <!-- User ID -->
+ <div class="form-item">
+ <cds-text-label for="user_id"
+ i18n
+ cdRequiredField="User ID"
+ [invalid]="userForm.controls.user_id.invalid && (userForm.controls.user_id.dirty)"
+ [invalidText]="userError">User ID
+ <input cdsText
+ formControlName="user_id"
+ name="user_id"
+ id="user_id"
+ [invalid]="userForm.controls.user_id.invalid && (userForm.controls.user_id.dirty)"
+ [readonly]="editing"
+ [autofocus]="!editing"/>
+ </cds-text-label>
+ <ng-template #userError>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('user_id', frm, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('user_id', frm, 'pattern')"
+ i18n>The value is not valid.</span>
+ <span class="invalid-feedback"
+ *ngIf="!userForm.getValue('show_tenant') && userForm.showError('user_id', frm, 'notUnique')"
+ i18n>The chosen user ID is already in use.</span>
+ </ng-template>
+ </div>
- <!-- Show Tenant -->
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="show_tenant"
- type="checkbox"
- (click)="updateFieldsWhenTenanted()"
- formControlName="show_tenant"
- [readonly]="true">
- <label class="custom-control-label spacing-03"
- for="show_tenant"
- i18n>Show Tenant</label>
- </div>
- </div>
- </div>
+ <!-- Show Tenant -->
+ <div class="form-item">
+ <cds-checkbox formControlName="show_tenant"
+ id="show_tenant"
+ [readonly]="true"
+ (checkedChange)="updateFieldsWhenTenanted()">Show Tenant
+ </cds-checkbox>
+ </div>
- <!-- Tenant -->
- <div class="form-group row"
- *ngIf="userForm.getValue('show_tenant')">
- <label class="cd-col-form-label"
- for="tenant"
- i18n>Tenant</label>
- <div class="cd-col-form-input">
- <input id="tenant"
- class="form-control"
- type="text"
- formControlName="tenant"
- [readonly]="editing"
- autofocus>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('tenant', frm, 'pattern')"
- i18n>The value is not valid.</span>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('tenant', frm, 'notUnique')"
- i18n>The chosen user ID exists in this tenant.</span>
- </div>
- </div>
+ <!-- Tenant -->
+ <div class="form-item"
+ *ngIf="userForm.getValue('show_tenant')">
+ <cds-text-label for="tenant"
+ i18n
+ [invalid]="userForm.controls.tenant.invalid && (userForm.controls.tenant.dirty)"
+ [invalidText]="tenantError">Tenant
+ <input cdsText
+ formControlName="tenant"
+ name="tenant"
+ id="tenant"
+ [invalid]="userForm.controls.tenant.invalid && (userForm.controls.tenant.dirty)"
+ [readonly]="editing"
+ autofocus/>
+ </cds-text-label>
+ <ng-template #tenantError>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('tenant', frm, 'pattern')"
+ i18n>The value is not valid.</span>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('tenant', frm, 'notUnique')"
+ i18n>The chosen user ID exists in this tenant.</span>
+ </ng-template>
+ </div>
- <!-- Full name -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- [ngClass]="{'required': !editing}"
- for="display_name"
- i18n>Full name</label>
- <div class="cd-col-form-input">
- <input id="display_name"
- class="form-control"
- type="text"
- formControlName="display_name">
- <span class="invalid-feedback"
- *ngIf="userForm.showError('display_name', frm, 'pattern')"
- i18n>The value is not valid.</span>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('display_name', frm, 'required')"
- i18n>This field is required.</span>
- </div>
- </div>
+ <!-- Full name -->
+ <div class="form-item">
+ <cds-text-label for="display_name"
+ i18n
+ cdRequiredField="Full name"
+ [invalid]="userForm.controls.display_name.invalid && (userForm.controls.display_name.dirty)"
+ [invalidText]="displayNameError">Full name
+ <input cdsText
+ formControlName="display_name"
+ name="display_name"
+ id="display_name"
+ [invalid]="userForm.controls.display_name.invalid && (userForm.controls.display_name.dirty)"
+ [readonly]="editing"/>
+ </cds-text-label>
+ <ng-template #displayNameError>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('display_name', frm, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('display_name', frm, 'pattern')"
+ i18n>The value is not valid.</span>
+ </ng-template>
+ </div>
- <!-- Email address -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- for="email"
- i18n>Email address</label>
- <div class="cd-col-form-input">
- <input id="email"
- class="form-control"
- type="text"
- formControlName="email">
- <span class="invalid-feedback"
- *ngIf="userForm.showError('email', frm, 'email')"
- i18n>This is not a valid email address.</span>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('email', frm, 'notUnique')"
- i18n>The chosen email address is already in use.</span>
- </div>
- </div>
+ <!-- Email address -->
+ <div class="form-item">
+ <cds-text-label for="email"
+ i18n
+ [invalid]="userForm.controls.email.invalid && (userForm.controls.email.dirty)"
+ [invalidText]="emailError">Email address
+ <input cdsText
+ formControlName="email"
+ name="email"
+ id="email"
+ [invalid]="userForm.controls.email.invalid && (userForm.controls.email.dirty)"/>
+ </cds-text-label>
+ <ng-template #emailError>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('email', frm, 'email')"
+ i18n>This is not a valid email address.</span>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('email', frm, 'notUnique')"
+ i18n>The chosen email address is already in use.</span>
+ </ng-template>
+ </div>
- <!-- Max. buckets -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- for="max_buckets_mode"
- i18n>Max. buckets</label>
- <div class="cd-col-form-input">
- <select class="form-select"
- formControlName="max_buckets_mode"
- name="max_buckets_mode"
- id="max_buckets_mode"
- (change)="onMaxBucketsModeChange($event.target.value)">
- <option i18n
- value="-1">Disabled</option>
- <option i18n
- value="0">Unlimited</option>
- <option i18n
- value="1">Custom</option>
- </select>
- </div>
- </div>
- <div *ngIf="1 == userForm.get('max_buckets_mode').value"
- class="form-group row">
- <label class="cd-col-form-label"></label>
- <div class="cd-col-form-input">
- <input id="max_buckets"
- class="form-control"
- type="number"
- formControlName="max_buckets"
- min="1">
- <span class="invalid-feedback"
- *ngIf="userForm.showError('max_buckets', frm, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('max_buckets', frm, 'min')"
- i18n>The entered value must be >= 1.</span>
- </div>
- </div>
+ <!-- Max. buckets -->
+ <div class="form-item">
+ <cds-select label="Maximum buckets"
+ for="max_buckets_mode"
+ formControlName="max_buckets_mode"
+ id="max_buckets_mode"
+ (valueChange)="onMaxBucketsModeChange($event)">
+ <option value="-1"
+ i18n>Disabled</option>
+ <option value="0"
+ i18n>Unlimited</option>
+ <option value="1"
+ i18n>Custom</option>
+ </cds-select>
+ </div>
+ <div *ngIf="1 == userForm.get('max_buckets_mode').value"
+ class="form-item">
+ <cds-number for="max_buckets"
+ formControlName="max_buckets"
+ id="max_buckets"
+ min="1"
+ [invalid]="userForm.controls.max_buckets.invalid && (userForm.controls.max_buckets.dirty)"
+ [invalidText]="maxBucketsError"></cds-number>
+ <ng-template #maxBucketsError>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('max_buckets', frm, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ i18n>The entered value must be >= 1.</span>
+ </ng-template>
+ </div>
- <!-- Suspended -->
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="suspended"
- type="checkbox"
- formControlName="suspended">
- <label class="custom-control-label spacing-03"
- for="suspended"
- i18n>Suspended</label>
- <cd-helper i18n>Suspending the user disables the user and subuser.</cd-helper>
- </div>
- </div>
- </div>
+ <!-- Suspended -->
+ <div class="form-item">
+ <cds-checkbox formControlName="suspended"
+ id="suspended"
+ i18n>Suspended
+ <cd-help-text>Suspending the user disables the user and subuser.</cd-help-text>
+ </cds-checkbox>
+ </div>
- <!-- System User -->
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="system"
- type="checkbox"
- formControlName="system">
- <label class="custom-control-label spacing-03"
- for="system"
- i18n>System user</label>
- <cd-helper i18n>System users are distinct from regular users, they are used by the RGW service to perform
- administrative tasks, manage buckets and objects</cd-helper>
- </div>
- </div>
- </div>
+ <!-- System User -->
+ <div class="form-item">
+ <cds-checkbox formControlName="system"
+ id="system"
+ i18n>System user
+ <cd-help-text>System users are distinct from regular users, they are used by the RGW service to perform administrative tasks, manage buckets and objects</cd-help-text>
+ </cds-checkbox>
+ </div>
- <!-- S3 key -->
- <fieldset *ngIf="!editing">
- <legend i18n>S3 key</legend>
-
- <!-- Auto-generate key -->
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="generate_key"
- type="checkbox"
- formControlName="generate_key">
- <label class="custom-control-label spacing-03"
- for="generate_key"
- i18n>Auto-generate key</label>
- </div>
- </div>
- </div>
+ <!-- S3 key -->
+ <fieldset *ngIf="!editing">
+ <legend i18n
+ class="cd-header">S3 key</legend>
- <!-- Access key -->
- <div class="form-group row"
- *ngIf="!editing && !userForm.getValue('generate_key')">
- <label class="cd-col-form-label required"
- for="access_key"
- i18n>Access key</label>
- <div class="cd-col-form-input">
- <div class="input-group">
- <input id="access_key"
- class="form-control"
- type="password"
- formControlName="access_key">
- <button type="button"
- class="btn btn-light"
- cdPasswordButton="access_key">
- </button>
- <cd-copy-2-clipboard-button source="access_key">
- </cd-copy-2-clipboard-button>
- </div>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('access_key', frm, 'required')"
- i18n>This field is required.</span>
- </div>
- </div>
+ <!-- Auto-generate key -->
+ <div class="form-item">
+ <cds-checkbox formControlName="generate_key"
+ id="generate_key"
+ i18n>Auto-generate key</cds-checkbox>
+ </div>
- <!-- Secret key -->
- <div class="form-group row"
- *ngIf="!editing && !userForm.getValue('generate_key')">
- <label class="cd-col-form-label required"
- for="secret_key"
- i18n>Secret key</label>
- <div class="cd-col-form-input">
- <div class="input-group">
- <input id="secret_key"
- class="form-control"
- type="password"
- formControlName="secret_key">
- <button type="button"
- class="btn btn-light"
- cdPasswordButton="secret_key">
- </button>
- <cd-copy-2-clipboard-button source="secret_key">
- </cd-copy-2-clipboard-button>
- </div>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('secret_key', frm, 'required')"
- i18n>This field
- is required.</span>
+ <!-- Access key -->
+ <div class="form-item form-item-append"
+ *ngIf="!editing && !userForm.getValue('generate_key')">
+ <cds-password-label for="access_key"
+ i18n
+ cdRequiredField="Access key"
+ [invalid]="userForm.controls.access_key.invalid && (userForm.controls.access_key.dirty)"
+ [invalidText]="accessKeyError">Access key
+ <input cdsPassword
+ type="password"
+ formControlName="access_key"
+ name="access_key"
+ id="access_key"
+ [invalid]="userForm.controls.access_key.invalid && (userForm.controls.access_key.dirty)"/>
+ </cds-password-label>
+ <cd-copy-2-clipboard-button source="access_key"
+ class="mt-4">
+ </cd-copy-2-clipboard-button>
+ <ng-template #accessKeyError>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('access_key', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
+ </div>
+
+ <!-- Secret key -->
+ <div class="form-item form-item-append"
+ *ngIf="!editing && !userForm.getValue('generate_key')">
+ <cds-password-label for="secret"
+ i18n
+ cdRequiredField="Secret key"
+ [invalid]="userForm.controls.secret_key.invalid && (userForm.controls.secret_key.dirty)"
+ [invalidText]="secretKeyError">Secret key
+ <input cdsPassword
+ type="password"
+ formControlName="secret_key"
+ name="secret_key"
+ id="secret_key"
+ [invalid]="userForm.controls.secret_key.invalid && (userForm.controls.secret_key.dirty)"/>
+ </cds-password-label>
+ <cd-copy-2-clipboard-button source="secret_key"
+ class="mt-4">
+ </cd-copy-2-clipboard-button>
+ <ng-template #secretKeyError>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('secret_key', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
+ </div>
+ </fieldset>
+
+ <!-- Subusers -->
+ <fieldset *ngIf="editing">
+ <div class="form-item">
+ <legend i18n
+ class="cd-header">Subusers</legend>
+ <span *ngIf="subusers.length === 0"
+ class="no-border">
+ <span class="form-text text-muted"
+ i18n>There are no subusers.</span>
+ </span>
+
+ <ng-container *ngFor="let subuser of subusers; let i=index;">
+
+ <div cdsRow
+ class="form-item-append"
+ [ngClass]="{'form-item': i > 0}">
+ <div cdsCol>
+ <cds-text-label>Subuser id
+ <input cdsText
+ [value]="subuser.id"
+ readonly>
+ </cds-text-label>
+ </div>
+ <div cdsCol>
+ <cds-text-label>Permissions
+ <input cdsText
+ [value]="('full-control' === subuser.permissions) ? 'full' : subuser.permissions"
+ readonly>
+ </cds-text-label>
+ </div>
+ <cds-icon-button kind="tertiary"
+ size="md"
+ title="Edit"
+ class="mt-4 tc_showSubuserButton"
+ (click)="showSubuserModal(i)">
+ <svg cdsIcon="edit"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </cds-icon-button>
+ <cds-icon-button kind="danger"
+ size="md"
+ title="Delete"
+ class="mt-4 tc_deleteSubuserButton"
+ (click)="deleteSubuser(i)">
+ <svg cdsIcon="trash-can"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </cds-icon-button>
+ </div>
+ </ng-container>
+ <div cdsRow
+ class="form-item">
+ <div cdsCol>
+ <button cdsButton="tertiary"
+ type="button"
+ class="tc_addSubuserButton"
+ (click)="showSubuserModal()">
+ <ng-container i18n>{{ actionLabels.CREATE | titlecase }}
+ {{ subuserLabel | upperFirst }}</ng-container>
+ <svg cdsIcon="add"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </button>
+ </div>
+ </div>
+ </div>
+ </fieldset>
+
+ <!-- Keys -->
+ <fieldset *ngIf="editing">
+ <div class="form-item">
+ <legend i18n
+ class="cd-header">Keys</legend>
+
+ <!-- S3 keys -->
+ <h6>S3</h6>
+ <span *ngIf="s3Keys.length === 0"
+ class="no-border">
+ <span class="form-text text-muted"
+ i18n>There are no keys.</span>
+ </span>
+
+ <span *ngFor="let key of s3Keys; let i=index;">
+ <div class="form-item-append"
+ cdsRow>
+ <div cdsCol>
+ <cds-text-label>
+ <input cdsText
+ [value]="key.user"
+ readonly>
+ </cds-text-label>
</div>
+ <cds-icon-button kind="tertiary"
+ size="md"
+ title="Show"
+ class="mt-2 tc_showS3KeyButton"
+ (click)="showS3KeyModal(i)">
+ <svg cdsIcon="view"
+ size="16"
+ class="cds--btn__icon"></svg>
+ </cds-icon-button>
+ <cds-icon-button kind="danger"
+ size="md"
+ title="Delete"
+ class="mt-2 tc_deleteS3KeyButton"
+ (click)="deleteS3Key(i)">
+ <svg cdsIcon="trash-can"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </cds-icon-button>
</div>
- </fieldset>
-
- <!-- Subusers -->
- <fieldset *ngIf="editing">
- <legend i18n>Subusers</legend>
- <div class="row">
- <div class="cd-col-form-offset">
- <span *ngIf="subusers.length === 0"
- class="no-border">
- <span class="form-text text-muted"
- i18n>There are no subusers.</span>
- </span>
-
- <span *ngFor="let subuser of subusers; let i=index;">
- <div class="input-group">
- <span class="input-group-text">
- <i class="{{ icons.user }}"></i>
- </span>
- <input type="text"
- class="cd-form-control"
- value="{{ subuser.id }}"
- readonly>
- <span class="input-group-text">
- <i class="{{ icons.share }}"></i>
- </span>
- <input type="text"
- class="cd-form-control"
- value="{{ ('full-control' === subuser.permissions) ? 'full' : subuser.permissions }}"
- readonly>
- <button type="button"
- class="btn btn-light tc_showSubuserButton"
- i18n-ngbTooltip
- ngbTooltip="Edit"
- (click)="showSubuserModal(i)">
- <i [ngClass]="[icons.edit]"></i>
- </button>
- <button type="button"
- class="btn btn-light tc_deleteSubuserButton"
- i18n-ngbTooltip
- ngbTooltip="Delete"
- (click)="deleteSubuser(i)">
- <i [ngClass]="[icons.destroy]"></i>
- </button>
- </div>
- <span class="form-text text-muted"></span>
- </span>
-
- <div class="row my-2">
- <div class="col-12">
- <button type="button"
- class="btn btn-light float-end tc_addSubuserButton"
- (click)="showSubuserModal()">
- <i [ngClass]="[icons.add]"></i>
- <ng-container i18n>{{ actionLabels.CREATE | titlecase }}
- {{ subuserLabel | upperFirst }}</ng-container>
- </button>
- </div>
- </div>
- <span class="help-block"></span>
- </div>
+ <span class="form-text text-muted"></span>
+ </span>
+
+ <div class="form-item"
+ cdsRow>
+ <div cdsCol>
+ <button type="button"
+ cdsButton="tertiary"
+ class="tc_addS3KeyButton"
+ (click)="showS3KeyModal()">
+ <ng-container i18n>{{ actionLabels.CREATE | titlecase }}
+ {{ s3keyLabel | upperFirst }}</ng-container>
+ <svg cdsIcon="add"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </button>
</div>
- </fieldset>
-
- <!-- Keys -->
- <fieldset *ngIf="editing">
- <legend i18n>Keys</legend>
-
- <!-- S3 keys -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- i18n>S3</label>
- <div class="cd-col-form-input">
- <span *ngIf="s3Keys.length === 0"
- class="no-border">
- <span class="form-text text-muted"
- i18n>There are no keys.</span>
- </span>
-
- <span *ngFor="let key of s3Keys; let i=index;">
- <div class="input-group">
- <div class="input-group-text">
- <i class="{{ icons.key }}"></i>
- </div>
- <input type="text"
- class="cd-form-control"
- value="{{ key.user }}"
- readonly>
- <button type="button"
- class="btn btn-light tc_showS3KeyButton"
- i18n-ngbTooltip
- ngbTooltip="Show"
- (click)="showS3KeyModal(i)">
- <i [ngClass]="[icons.show]"></i>
- </button>
- <button type="button"
- class="btn btn-light tc_deleteS3KeyButton"
- i18n-ngbTooltip
- ngbTooltip="Delete"
- (click)="deleteS3Key(i)">
- <i [ngClass]="[icons.destroy]"></i>
- </button>
- </div>
- <span class="form-text text-muted"></span>
- </span>
-
- <div class="row my-2">
- <div class="col-12">
- <button type="button"
- class="btn btn-light float-end tc_addS3KeyButton"
- (click)="showS3KeyModal()">
- <i [ngClass]="[icons.add]"></i>
- <ng-container i18n>{{ actionLabels.CREATE | titlecase }}
- {{ s3keyLabel | upperFirst }}</ng-container>
- </button>
- </div>
- </div>
-
- <span class="help-block"></span>
- </div>
+ </div>
- <hr>
- </div>
+ <hr>
+ </div>
- <!-- Swift keys -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- i18n>Swift</label>
-
- <div class="cd-col-form-input">
- <span *ngIf="swiftKeys.length === 0"
- class="no-border">
- <span class="form-text text-muted"
- i18n>There are no keys.</span>
- </span>
-
- <span *ngFor="let key of swiftKeys; let i=index;">
- <div class="input-group">
- <span class="input-group-text">
- <i class="{{ icons.key }}"></i>
- </span>
- <input type="text"
- class="cd-form-control"
- value="{{ key.user }}"
- readonly>
- <button type="button"
- class="btn btn-light tc_showSwiftKeyButton"
- i18n-ngbTooltip
- ngbTooltip="Show"
- (click)="showSwiftKeyModal(i)">
- <i [ngClass]="[icons.show]"></i>
- </button>
- </div>
- <span class="form-text text-muted"></span>
- </span>
+ <!-- Swift keys -->
+ <div class="form-item">
+
+ <h6>Swift</h6>
+ <span *ngIf="swiftKeys.length === 0"
+ class="no-border">
+ <span class="form-text text-muted"
+ i18n>There are no keys.</span>
+ </span>
+
+ <span *ngFor="let key of swiftKeys; let i=index;">
+ <div class="form-item-append"
+ cdsRow>
+ <div cdsCol>
+ <cds-text-label>
+ <input cdsText
+ [value]="key.user"
+ readonly>
+ </cds-text-label>
</div>
+ <cds-icon-button kind="tertiary"
+ size="md"
+ title="Show"
+ class="mt-2 tc_showSwiftKeyButton"
+ (click)="showSwiftKeyModal(i)">
+ <svg cdsIcon="view"
+ size="16"
+ class="cds--btn__icon"></svg>
+ </cds-icon-button>
</div>
- </fieldset>
-
- <!-- Capabilities -->
- <fieldset *ngIf="editing">
- <legend i18n>Capabilities</legend>
-
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <span *ngIf="capabilities.length === 0"
- class="no-border">
- <span class="form-text text-muted"
- i18n>There are no capabilities.</span>
- </span>
-
- <span *ngFor="let cap of capabilities; let i=index;">
- <div class="input-group">
- <div class="input-group-text">
- <i class="{{ icons.share }}"></i>
- </div>
- <input type="text"
- class="cd-form-control"
- value="{{ cap.type }}:{{ cap.perm }}"
- readonly>
- <button type="button"
- class="btn btn-light tc_editCapButton"
- i18n-ngbTooltip
- ngbTooltip="Edit"
- (click)="showCapabilityModal(i)">
- <i [ngClass]="[icons.edit]"></i>
- </button>
- <button type="button"
- class="btn btn-light tc_deleteCapButton"
- i18n-ngbTooltip
- ngbTooltip="Delete"
- (click)="deleteCapability(i)">
- <i [ngClass]="[icons.destroy]"></i>
- </button>
- </div>
- <span class="form-text text-muted"></span>
- </span>
-
- <div class="row my-2">
- <div class="col-12">
- <button type="button"
- class="btn btn-light float-end tc_addCapButton"
- [disabled]="capabilities | pipeFunction:hasAllCapabilities"
- i18n-ngbTooltip
- ngbTooltip="All capabilities are already added."
- [disableTooltip]="!(capabilities | pipeFunction:hasAllCapabilities)"
- triggers="pointerenter:pointerleave"
- (click)="showCapabilityModal()">
- <i [ngClass]="[icons.add]"></i>
- <ng-container i18n>{{ actionLabels.ADD | titlecase }}
- {{ capabilityLabel | upperFirst }}</ng-container>
- </button>
- </div>
- </div>
- <span class="help-block"></span>
+ </span>
+ </div>
+ </fieldset>
+
+ <!-- Capabilities -->
+ <fieldset *ngIf="editing">
+
+ <div class="form-item">
+ <legend i18n
+ class="cd-header">Capabilities</legend>
+ <span *ngIf="capabilities.length === 0"
+ class="no-border">
+ <span class="form-text text-muted"
+ i18n>There are no capabilities.</span>
+ </span>
+
+ <span *ngFor="let cap of capabilities; let i=index;">
+ <div class="form-item-append"
+ [ngClass]="{'form-item': i > 0}"
+ cdsRow>
+ <div cdsCol>
+ <cds-text-label i18n>Type
+ <input cdsText
+ [value]="cap.type"
+ readonly>
+ </cds-text-label>
</div>
- </div>
- </fieldset>
-
- <!-- User quota -->
- <fieldset>
- <legend i18n>User quota</legend>
-
- <!-- Enabled -->
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="user_quota_enabled"
- type="checkbox"
- formControlName="user_quota_enabled">
- <label class="custom-control-label spacing-03"
- for="user_quota_enabled"
- i18n>Enabled</label>
- </div>
+ <div cdsCol>
+ <cds-text-label i18n>Permission
+ <input cdsText
+ [value]="cap.perm"
+ readonly>
+ </cds-text-label>
</div>
+ <cds-icon-button kind="tertiary"
+ size="md"
+ title="Edit"
+ class="mt-4 tc_editCapButton"
+ (click)="showCapabilityModal(i)">
+ <svg cdsIcon="edit"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </cds-icon-button>
+ <cds-icon-button kind="danger"
+ size="md"
+ title="Delete"
+ class="mt-4 tc_deleteCapButton"
+ (click)="deleteCapability(i)">
+ <svg cdsIcon="trash-can"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </cds-icon-button>
</div>
-
- <!-- Unlimited size -->
- <div class="form-group row"
- *ngIf="userForm.controls.user_quota_enabled.value">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="user_quota_max_size_unlimited"
- type="checkbox"
- formControlName="user_quota_max_size_unlimited">
- <label class="custom-control-label spacing-03"
- for="user_quota_max_size_unlimited"
- i18n>Unlimited size</label>
- </div>
- </div>
+ <span class="form-text text-muted"></span>
+ </span>
+
+ <div class="form-item"
+ cdsRow>
+ <div cdsCol>
+ <button type="button"
+ class="tc_addCapButton"
+ cdsButton="tertiary"
+ [disabled]="capabilities | pipeFunction:hasAllCapabilities"
+ i18n-ngbTooltip
+ ngbTooltip="All capabilities are already added."
+ [disableTooltip]="!(capabilities | pipeFunction:hasAllCapabilities)"
+ triggers="pointerenter:pointerleave"
+ (click)="showCapabilityModal()">
+ <ng-container i18n>{{ actionLabels.ADD | titlecase }}
+ {{ capabilityLabel | upperFirst }}</ng-container>
+ <svg cdsIcon="add"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </button>
</div>
+ </div>
+ </div>
+ </fieldset>
+
+ <!-- User quota -->
+ <fieldset>
+ <div class="form-item">
+ <legend i18n
+ class="cd-header">User quota</legend>
+
+ <!-- Enabled -->
+ <cds-checkbox formControlName="user_quota_enabled"
+ id="user_quota_enabled"
+ i18n>Enabled
+ </cds-checkbox>
+ </div>
- <!-- Maximum size -->
- <div class="form-group row"
- *ngIf="userForm.controls.user_quota_enabled.value && !userForm.getValue('user_quota_max_size_unlimited')">
- <label class="cd-col-form-label required"
- for="user_quota_max_size"
- i18n>Max. size</label>
- <div class="cd-col-form-input">
- <input id="user_quota_max_size"
- class="form-control"
- type="text"
- formControlName="user_quota_max_size"
- cdDimlessBinary>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('user_quota_max_size', frm, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('user_quota_max_size', frm, 'quotaMaxSize')"
- i18n>The value is not valid.</span>
- <span *ngIf="userForm.showError('user_quota_max_size', formDir, 'pattern')"
- class="invalid-feedback"
- i18n>Size must be a number or in a valid format. eg: 5 GiB</span>
- </div>
- </div>
+ <!-- Unlimited size -->
+ <div class="form-item"
+ *ngIf="userForm.controls.user_quota_enabled.value">
+ <cds-checkbox formControlName="user_quota_max_size_unlimited"
+ id="user_quota_max_size_unlimited"
+ i18n>Unlimited size</cds-checkbox>
+ </div>
- <!-- Unlimited objects -->
- <div class="form-group row"
- *ngIf="userForm.controls.user_quota_enabled.value">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="user_quota_max_objects_unlimited"
- type="checkbox"
- formControlName="user_quota_max_objects_unlimited">
- <label class="custom-control-label spacing-03"
- for="user_quota_max_objects_unlimited"
- i18n>Unlimited objects</label>
- </div>
- </div>
- </div>
+ <!-- Maximum size -->
+ <div class="form-item"
+ *ngIf="userForm.controls.user_quota_enabled.value && !userForm.getValue('user_quota_max_size_unlimited')">
+ <cds-text-label for="user_quota_max_size"
+ i18n
+ cdRequiredField="Maximum size"
+ [invalid]="userForm.controls.user_quota_max_size.invalid && (userForm.controls.user_quota_max_size.dirty)"
+ [invalidText]="quotaMaxSizeError">Maximum size
+ <input cdsText
+ formControlName="user_quota_max_size"
+ name="user_quota_max_size"
+ id="user_quota_max_size"
+ [invalid]="userForm.controls.user_quota_max_size.invalid && (userForm.controls.user_quota_max_size.dirty)"
+ cdDimlessBinary/>
+ </cds-text-label>
+ <ng-template #quotaMaxSizeError>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('user_quota_max_size', frm, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('user_quota_max_size', frm, 'quotaMaxSize')"
+ i18n>The value is not valid.</span>
+ <span *ngIf="userForm.showError('user_quota_max_size', formDir, 'pattern')"
+ class="invalid-feedback"
+ i18n>Size must be a number or in a valid format. eg: 5 GiB</span>
+ </ng-template>
+ </div>
- <!-- Maximum objects -->
- <div class="form-group row"
- *ngIf="userForm.controls.user_quota_enabled.value && !userForm.getValue('user_quota_max_objects_unlimited')">
- <label class="cd-col-form-label required"
- for="user_quota_max_objects"
- i18n>Max. objects</label>
- <div class="cd-col-form-input">
- <input id="user_quota_max_objects"
- class="form-control"
- type="number"
- formControlName="user_quota_max_objects"
- min="0">
- <span class="invalid-feedback"
- *ngIf="userForm.showError('user_quota_max_objects', frm, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('user_quota_max_objects', frm, 'min')"
- i18n>The entered value must be >= 0.</span>
- </div>
- </div>
- </fieldset>
-
- <!-- Bucket quota -->
- <fieldset>
- <legend i18n>Bucket quota</legend>
-
- <!-- Enabled -->
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="bucket_quota_enabled"
- type="checkbox"
- formControlName="bucket_quota_enabled">
- <label class="custom-control-label spacing-03"
- for="bucket_quota_enabled"
- i18n>Enabled</label>
- </div>
- </div>
- </div>
+ <!-- Unlimited objects -->
+ <div class="form-item"
+ *ngIf="userForm.controls.user_quota_enabled.value">
+ <cds-checkbox formControlName="user_quota_max_objects_unlimited"
+ id="user_quota_max_objects_unlimited"
+ i18n>Unlimited objects</cds-checkbox>
+ </div>
- <!-- Unlimited size -->
- <div class="form-group row"
- *ngIf="userForm.controls.bucket_quota_enabled.value">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="bucket_quota_max_size_unlimited"
- type="checkbox"
- formControlName="bucket_quota_max_size_unlimited">
- <label class="custom-control-label spacing-03"
- for="bucket_quota_max_size_unlimited"
- i18n>Unlimited size</label>
- </div>
- </div>
- </div>
+ <!-- Maximum objects -->
+ <div class="form-item"
+ *ngIf="userForm.controls.user_quota_enabled.value && !userForm.getValue('user_quota_max_objects_unlimited')">
+ <cds-number for="user_quota_max_objects"
+ formControlName="user_quota_max_objects"
+ id="user_quota_max_objects"
+ [min]="0"
+ [invalid]="userForm.controls.user_quota_max_objects.invalid && (userForm.controls.user_quota_max_objects.dirty)"
+ [invalidText]="maxObjectsError"
+ label="Maximum Objects"
+ cdRequiredField="Maximum Objects"></cds-number>
+ <ng-template #maxObjectsError>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('user_quota_max_objects', frm, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('user_quota_max_objects', frm, 'min')"
+ i18n>The entered value must be >= 0.</span>
+ </ng-template>
+ </div>
+ </fieldset>
+
+ <!-- Bucket quota -->
+ <fieldset>
+ <div class="form-item">
+ <legend i18n
+ class="cd-header">Bucket quota</legend>
+
+ <!-- Enabled -->
+ <cds-checkbox formControlName="bucket_quota_enabled"
+ id="bucket_quota_enabled"
+ i18n>Enabled</cds-checkbox>
+ </div>
- <!-- Maximum size -->
- <div class="form-group row"
- *ngIf="userForm.controls.bucket_quota_enabled.value && !userForm.getValue('bucket_quota_max_size_unlimited')">
- <label class="cd-col-form-label required"
- for="bucket_quota_max_size"
- i18n>Max. size</label>
- <div class="cd-col-form-input">
- <input id="bucket_quota_max_size"
- class="form-control"
- type="text"
- formControlName="bucket_quota_max_size"
- cdDimlessBinary>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('bucket_quota_max_size', frm, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('bucket_quota_max_size', frm, 'quotaMaxSize')"
- i18n>The value is not valid.</span>
- <span *ngIf="userForm.showError('bucket_quota_max_size', formDir, 'pattern')"
- class="invalid-feedback"
- i18n>Size must be a number or in a valid format. eg: 5 GiB</span>
- </div>
- </div>
+ <!-- Unlimited size -->
+ <div class="form-item"
+ *ngIf="userForm.controls.bucket_quota_enabled.value">
+ <cds-checkbox formControlName="bucket_quota_max_size_unlimited"
+ id="bucket_quota_max_size_unlimited"
+ i18n>Unlimited size</cds-checkbox>
+ </div>
- <!-- Unlimited objects -->
- <div class="form-group row"
- *ngIf="userForm.controls.bucket_quota_enabled.value">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="bucket_quota_max_objects_unlimited"
- type="checkbox"
- formControlName="bucket_quota_max_objects_unlimited">
- <label class="custom-control-label spacing-03"
- for="bucket_quota_max_objects_unlimited"
- i18n>Unlimited objects</label>
- </div>
- </div>
- </div>
+ <!-- Maximum size -->
+ <div class="form-item"
+ *ngIf="userForm.controls.bucket_quota_enabled.value && !userForm.getValue('bucket_quota_max_size_unlimited')">
+ <cds-text-label for="bucket_quota_max_size"
+ i18n
+ cdRequiredField="Maximum size"
+ [invalid]="userForm.controls.bucket_quota_max_size.invalid && (userForm.controls.bucket_quota_max_size.dirty)"
+ [invalidText]="bucketQuotaMaxSizeError">Maximum size
+ <input cdsText
+ formControlName="bucket_quota_max_size"
+ name="bucket_quota_max_size"
+ id="bucket_quota_max_size"
+ [invalid]="userForm.controls.bucket_quota_max_size.invalid && (userForm.controls.bucket_quota_max_size.dirty)"
+ cdDimlessBinary/>
+ </cds-text-label>
+ <ng-template #bucketQuotaMaxSizeError>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('bucket_quota_max_size', frm, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('bucket_quota_max_size', frm, 'quotaMaxSize')"
+ i18n>The value is not valid.</span>
+ <span *ngIf="userForm.showError('bucket_quota_max_size', formDir, 'pattern')"
+ class="invalid-feedback"
+ i18n>Size must be a number or in a valid format. eg: 5 GiB</span>
+ </ng-template>
+ </div>
- <!-- Maximum objects -->
- <div class="form-group row"
- *ngIf="userForm.controls.bucket_quota_enabled.value && !userForm.getValue('bucket_quota_max_objects_unlimited')">
- <label class="cd-col-form-label required"
- for="bucket_quota_max_objects"
- i18n>Max. objects</label>
- <div class="cd-col-form-input">
- <input id="bucket_quota_max_objects"
- class="form-control"
- type="number"
- formControlName="bucket_quota_max_objects"
- min="0">
- <span class="invalid-feedback"
- *ngIf="userForm.showError('bucket_quota_max_objects', frm, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="userForm.showError('bucket_quota_max_objects', frm, 'min')"
- i18n>Enter a positive number.</span>
- </div>
- </div>
- </fieldset>
-
- <!-- Advanced Section -->
- <cd-form-advanced-fieldset>
- <!-- User Rate Limit -->
- <cd-rgw-rate-limit [type]="'user'"
- [isEditing]="this.editing"
- [id]="uid"
- (rateLimitFormGroup)="rateLimitFormInit($event)">
- </cd-rgw-rate-limit>
- </cd-form-advanced-fieldset>
+ <!-- Unlimited objects -->
+ <div class="form-item"
+ *ngIf="userForm.controls.bucket_quota_enabled.value">
+ <cds-checkbox formControlName="bucket_quota_max_objects_unlimited"
+ id="bucket_quota_max_objects_unlimited"
+ i18n>Unlimited objects</cds-checkbox>
</div>
- <div class="card-footer">
- <cd-form-button-panel (submitActionEvent)="onSubmit()"
- [form]="userForm"
- [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"
- wrappingClass="text-right"></cd-form-button-panel>
+ <!-- Maximum objects -->
+ <div class="form-item"
+ *ngIf="userForm.controls.bucket_quota_enabled.value && !userForm.getValue('bucket_quota_max_objects_unlimited')">
+ <cds-number for="bucket_quota_max_objects"
+ formControlName="bucket_quota_max_objects"
+ id="bucket_quota_max_objects"
+ [min]="0"
+ [invalid]="userForm.controls.bucket_quota_max_objects.invalid && ( userForm.controls.bucket_quota_max_objects.dirty)"
+ [invalidText]="bucketMaxObjectsError"
+ label="Maximum objects"
+ cdRequiredField="Maximum objects"></cds-number>
+ <ng-template #bucketMaxObjectsError>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('bucket_quota_max_objects', frm, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="userForm.showError('bucket_quota_max_objects', frm, 'min')"
+ i18n>The entered value must be >= 0.</span>
+ </ng-template>
</div>
- </div>
+ </fieldset>
+
+ <!-- Advanced Section -->
+ <cd-form-advanced-fieldset>
+ <!-- User Rate Limit -->
+ <cd-rgw-rate-limit [type]="'user'"
+ [isEditing]="this.editing"
+ [id]="uid"
+ (rateLimitFormGroup)="rateLimitFormInit($event)">
+ </cd-rgw-rate-limit>
+ </cd-form-advanced-fieldset>
+
+ <cd-form-button-panel (submitActionEvent)="onSubmit()"
+ [form]="userForm"
+ [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"
+ wrappingClass="text-right"></cd-form-button-panel>
</form>
</div>
import { FormatterService } from '~/app/shared/services/formatter.service';
import { RgwRateLimitComponent } from '../rgw-rate-limit/rgw-rate-limit.component';
import { By } from '@angular/platform-browser';
+import { CheckboxModule, NumberModule, SelectModule } from 'carbon-components-angular';
describe('RgwUserFormComponent', () => {
let component: RgwUserFormComponent;
SharedModule,
ToastrModule.forRoot(),
NgbTooltipModule,
- PipesModule
+ PipesModule,
+ CheckboxModule,
+ NumberModule,
+ SelectModule
]
});
it('should call showCapabilityModal', () => {
const modalShowSpy = spyOn(component['modalService'], 'show').and.callFake(() => {
modalRef = {
- componentInstance: {
- setEditing: jest.fn(),
- setValues: jest.fn(),
- setCapabilities: jest.fn(),
- submitAction: { subscribe: jest.fn() }
- }
+ setEditing: jest.fn(),
+ setValues: jest.fn(),
+ setCapabilities: jest.fn(),
+ submitAction: { subscribe: jest.fn() }
};
return modalRef;
});
it('should call showSwiftKeyModal', () => {
const modalShowSpy = spyOn(component['modalService'], 'show').and.callFake(() => {
modalRef = {
- componentInstance: {
- setValues: jest.fn()
- }
+ setValues: jest.fn()
};
return modalRef;
});
it('should call showS3KeyModal', () => {
const modalShowSpy = spyOn(component['modalService'], 'show').and.callFake(() => {
modalRef = {
- componentInstance: {
- setValues: jest.fn(),
- setViewing: jest.fn(),
- setUserCandidates: jest.fn(),
- submitAction: { subscribe: jest.fn() }
- }
+ setValues: jest.fn(),
+ setViewing: jest.fn(),
+ setUserCandidates: jest.fn(),
+ submitAction: { subscribe: jest.fn() }
};
return modalRef;
});
it('should call showSubuserModal', () => {
const modalShowSpy = spyOn(component['modalService'], 'show').and.callFake(() => {
modalRef = {
- componentInstance: {
- setValues: jest.fn(),
- setViewing: jest.fn(),
- setEditing: jest.fn(),
- setUserCandidates: jest.fn(),
- submitAction: { subscribe: jest.fn() }
- }
+ setValues: jest.fn(),
+ setViewing: jest.fn(),
+ setEditing: jest.fn(),
+ setUserCandidates: jest.fn(),
+ submitAction: { subscribe: jest.fn() }
};
return modalRef;
});
];
let spy = spyOn(component['modalService'], 'show').and.callFake(() => {
return (modalRef = {
- componentInstance: {
- setEditing: jest.fn(),
- setValues: jest.fn(),
- setCapabilities: jest.fn(),
- setSubusers: jest.fn(),
- setUserCandidates: jest.fn(),
- submitAction: { subscribe: jest.fn() }
- }
+ setEditing: jest.fn(),
+ setValues: jest.fn(),
+ setCapabilities: jest.fn(),
+ setSubusers: jest.fn(),
+ setUserCandidates: jest.fn(),
+ submitAction: { subscribe: jest.fn() }
});
});
spyOn(component, 'getUID').and.returnValue('dashboard');
component.showSubuserModal(index);
expect(spy).toHaveBeenCalledTimes(1);
- expect(modalRef.componentInstance.setEditing).toHaveBeenCalledTimes(1);
- expect(modalRef.componentInstance.setValues).toHaveBeenCalledWith(
+ expect(modalRef.setEditing).toHaveBeenCalledTimes(1);
+ expect(modalRef.setValues).toHaveBeenCalledWith(
'dashboard',
component.subusers[index].id,
component.subusers[index].permissions
);
- expect(modalRef.componentInstance.submitAction.subscribe).toHaveBeenCalled();
+ expect(modalRef.submitAction.subscribe).toHaveBeenCalled();
});
it('should handle "Add" scenario when index is not provided', () => {
let spy = spyOn(component['modalService'], 'show').and.callFake(() => {
return (modalRef = {
- componentInstance: {
- setEditing: jest.fn(),
- setValues: jest.fn(),
- setCapabilities: jest.fn(),
- setSubusers: jest.fn(),
- setUserCandidates: jest.fn(),
- submitAction: { subscribe: jest.fn() }
- }
+ setEditing: jest.fn(),
+ setValues: jest.fn(),
+ setCapabilities: jest.fn(),
+ setSubusers: jest.fn(),
+ setUserCandidates: jest.fn(),
+ submitAction: { subscribe: jest.fn() }
});
});
component.subusers = [
spyOn(component, 'getUID').and.returnValue('dashboard');
component.showSubuserModal();
expect(spy).toHaveBeenCalledTimes(1);
- expect(modalRef.componentInstance.setEditing).toHaveBeenCalledWith(false);
- expect(modalRef.componentInstance.setValues).toHaveBeenCalledWith('dashboard');
- expect(modalRef.componentInstance.setSubusers).toHaveBeenCalledWith(component.subusers);
- expect(modalRef.componentInstance.submitAction.subscribe).toHaveBeenCalled();
+ expect(modalRef.setEditing).toHaveBeenCalledWith(false);
+ expect(modalRef.setValues).toHaveBeenCalledWith('dashboard');
+ expect(modalRef.setSubusers).toHaveBeenCalledWith(component.subusers);
+ expect(modalRef.submitAction.subscribe).toHaveBeenCalled();
});
});
});
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { CdValidators } from '~/app/shared/forms/cd-validators';
import { FormatterService } from '~/app/shared/services/formatter.service';
-import { ModalService } from '~/app/shared/services/modal.service';
import { NotificationService } from '~/app/shared/services/notification.service';
import { RgwUserCapabilities } from '../models/rgw-user-capabilities';
import { RgwUserCapability } from '../models/rgw-user-capability';
import { RgwUserSwiftKeyModalComponent } from '../rgw-user-swift-key-modal/rgw-user-swift-key-modal.component';
import { RgwRateLimitComponent } from '../rgw-rate-limit/rgw-rate-limit.component';
import { RgwRateLimitConfig } from '../models/rgw-rate-limit';
+import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
@Component({
selector: 'cd-rgw-user-form',
private route: ActivatedRoute,
private router: Router,
private rgwUserService: RgwUserService,
- private modalService: ModalService,
+ private modalService: ModalCdsService,
private notificationService: NotificationService,
public actionLabels: ActionLabelsI18n
) {
if (_.isNumber(index)) {
// Edit
const subuser = this.subusers[index];
- modalRef.componentInstance.setEditing();
- modalRef.componentInstance.setValues(uid, subuser.id, subuser.permissions);
+ modalRef.setEditing();
+ modalRef.setValues(uid, subuser.id, subuser.permissions);
} else {
// Add
- modalRef.componentInstance.setEditing(false);
- modalRef.componentInstance.setValues(uid);
- modalRef.componentInstance.setSubusers(this.subusers);
+ modalRef.setEditing(false);
+ modalRef.setValues(uid);
+ modalRef.setSubusers(this.subusers);
}
- modalRef.componentInstance.submitAction.subscribe((subuser: RgwUserSubuser) => {
+ modalRef.submitAction.subscribe((subuser: RgwUserSubuser) => {
this.setSubuser(subuser, index);
});
}
if (_.isNumber(index)) {
// View
const key = this.s3Keys[index];
- modalRef.componentInstance.setViewing();
- modalRef.componentInstance.setValues(key.user, key.access_key, key.secret_key);
+ modalRef.setViewing();
+ modalRef.setValues(key.user, key.access_key, key.secret_key);
} else {
// Add
const candidates = this._getS3KeyUserCandidates();
- modalRef.componentInstance.setViewing(false);
- modalRef.componentInstance.setUserCandidates(candidates);
- modalRef.componentInstance.submitAction.subscribe((key: RgwUserS3Key) => {
+ modalRef.setViewing(false);
+ modalRef.setUserCandidates(candidates);
+ modalRef.submitAction.subscribe((key: RgwUserS3Key) => {
this.setS3Key(key);
});
}
showSwiftKeyModal(index: number) {
const modalRef = this.modalService.show(RgwUserSwiftKeyModalComponent);
const key = this.swiftKeys[index];
- modalRef.componentInstance.setValues(key.user, key.secret_key);
+ modalRef.setValues(key.user, key.secret_key);
}
/**
if (_.isNumber(index)) {
// Edit
const cap = this.capabilities[index];
- modalRef.componentInstance.setEditing();
- modalRef.componentInstance.setValues(cap.type, cap.perm);
+ modalRef.setEditing();
+ modalRef.setValues(cap.type, cap.perm);
} else {
// Add
- modalRef.componentInstance.setEditing(false);
- modalRef.componentInstance.setCapabilities(this.capabilities);
+ modalRef.setEditing(false);
+ modalRef.setCapabilities(this.capabilities);
}
- modalRef.componentInstance.submitAction.subscribe((cap: RgwUserCapability) => {
+ modalRef.submitAction.subscribe((cap: RgwUserCapability) => {
this.setCapability(cap, index);
});
}
-<cd-modal [modalRef]="activeModal">
- <ng-container i18n="form title"
- class="modal-title">{{ action | titlecase }} {{ resource | upperFirst }}</ng-container>
+<cds-modal size="md"
+ [open]="open"
+ [hasScrollingContent]="false"
+ (overlaySelected)="closeModal()">
+ <cds-modal-header (closeSelect)="closeModal()">
+ <ng-container i18n="form title"
+ class="modal-title">{{ action | titlecase }} {{ resource | upperFirst }}</ng-container>
- <ng-container class="modal-content">
- <form #frm="ngForm"
- [formGroup]="formGroup"
- novalidate>
- <div class="modal-body">
+ <cd-help-text [formAllFieldsRequired]="true"></cd-help-text>
+ </cds-modal-header>
- <!-- Username -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- [ngClass]="{'required': !viewing}"
- for="user"
- i18n>Username</label>
- <div class="cd-col-form-input">
- <input id="user"
- class="form-control"
- type="text"
- *ngIf="viewing"
- [readonly]="true"
- formControlName="user">
- <select id="user"
- class="form-control"
+ <form #frm="ngForm"
+ [formGroup]="formGroup"
+ novalidate>
+ <div cdsModalContent>
+
+ <!-- Username -->
+ <div class="form-item">
+ <cds-text-label for="user"
+ [invalid]="formGroup.controls.user.invalid && formGroup.controls.user.dirty"
+ [invalidText]="userError"
+ *ngIf="viewing"
+ i18n>Username
+ <input cdsText
+ id="user"
+ name="user"
+ formControlName="user"
+ readonly
+ [readonly]="true"
+ modal-primary-focus>
+ </cds-text-label>
+
+ <cds-select *ngIf="!viewing"
+ label="Username"
+ i18n-label
+ for="user"
formControlName="user"
- *ngIf="!viewing"
- autofocus>
- <option i18n
- *ngIf="userCandidates !== null"
- [ngValue]="null">-- Select a username --</option>
- <option *ngFor="let userCandidate of userCandidates"
- [value]="userCandidate">{{ userCandidate }}</option>
- </select>
- <span class="invalid-feedback"
- *ngIf="formGroup.showError('user', frm, 'required')"
- i18n>This field is required.</span>
- </div>
- </div>
+ [invalid]="formGroup.controls.user.invalid && formGroup.controls.user.dirty"
+ [invalidText]="userError">
+ <option value="">--- Select a username ---</option>
+ <option *ngFor="let userCandidate of userCandidates"
+ [value]="userCandidate">{{ userCandidate }}</option>
+ </cds-select>
- <!-- Auto-generate key -->
- <div class="form-group row"
- *ngIf="!viewing">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="generate_key"
- type="checkbox"
- formControlName="generate_key">
- <label class="custom-control-label"
- for="generate_key"
- i18n>Auto-generate key</label>
- </div>
- </div>
- </div>
+ <ng-template #userError>
+ <span class="invalid-feedback"
+ *ngIf="formGroup.showError('user', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
+ </div>
- <!-- Access key -->
- <div class="form-group row"
- *ngIf="!formGroup.getValue('generate_key')">
- <label class="cd-col-form-label"
- [ngClass]="{'required': !viewing}"
- for="access_key"
- i18n>Access key</label>
- <div class="cd-col-form-input">
- <div class="input-group">
- <input id="access_key"
- class="form-control"
- type="password"
- [readonly]="viewing"
- formControlName="access_key">
- <button type="button"
- class="btn btn-light"
- cdPasswordButton="access_key">
- </button>
- <cd-copy-2-clipboard-button source="access_key">
- </cd-copy-2-clipboard-button>
- </div>
- <span class="invalid-feedback"
- *ngIf="formGroup.showError('access_key', frm, 'required')"
- i18n>This field is required.</span>
- </div>
- </div>
+ <!-- Auto-generate key -->
+ <div class="form-item"
+ *ngIf="!viewing">
+ <cds-checkbox id="generate_key"
+ formControlName="generate_key"
+ i18n>Auto-generate key
+ </cds-checkbox>
+ </div>
- <!-- Secret key -->
- <div class="form-group row"
- *ngIf="!formGroup.getValue('generate_key')">
- <label class="cd-col-form-label"
- [ngClass]="{'required': !viewing}"
- for="secret_key"
- i18n>Secret key</label>
- <div class="cd-col-form-input">
- <div class="input-group">
- <input id="secret_key"
- class="form-control"
- type="password"
- [readonly]="viewing"
- formControlName="secret_key">
- <button type="button"
- class="btn btn-light"
- cdPasswordButton="secret_key">
- </button>
- <cd-copy-2-clipboard-button source="secret_key">
- </cd-copy-2-clipboard-button>
- </div>
- <span class="invalid-feedback"
- *ngIf="formGroup.showError('secret_key', frm, 'required')"
- i18n>This field is required.</span>
- </div>
- </div>
+ <!-- Access key -->
+ <div class="form-item form-item-append"
+ *ngIf="!formGroup.getValue('generate_key')">
+ <cds-password-label for="access_key"
+ [invalid]="formGroup.controls.access_key.invalid && formGroup.controls.access_key.dirty"
+ [invalidText]="accessKeyError"
+ i18n>Access key
+ <input cdsPassword
+ id="access_key"
+ name="access_key"
+ formControlName="access_key"
+ [readonly]="viewing">
+ </cds-password-label>
+ <cd-copy-2-clipboard-button source="access_key"
+ class="mt-4">
+ </cd-copy-2-clipboard-button>
+ <ng-template #accessKeyError>
+ <span class="invalid-feedback"
+ *ngIf="formGroup.showError('access_key', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
</div>
- <div class="modal-footer">
- <cd-form-button-panel (submitActionEvent)="onSubmit()"
- [form]="formGroup"
- [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"
- [showSubmit]="!viewing"></cd-form-button-panel>
+ <!-- Secret key -->
+ <div class="form-item form-item-append"
+ *ngIf="!formGroup.getValue('generate_key')">
+ <cds-password-label for="secret_key"
+ [invalid]="formGroup.controls.secret_key.invalid && formGroup.controls.secret_key.dirty"
+ [invalidText]="secretKeyError"
+ i18n>Secret key
+ <input cdsPassword
+ id="secret_key"
+ name="secret_key"
+ formControlName="secret_key"
+ [invalid]="formGroup.controls.secret_key.invalid && formGroup.controls.secret_key.dirty"
+ [readonly]="viewing">
+ </cds-password-label>
+
+ <cd-copy-2-clipboard-button source="secret_key"
+ class="mt-4">
+ </cd-copy-2-clipboard-button>
+
+ <ng-template #secretKeyError>
+ <span class="invalid-feedback"
+ *ngIf="formGroup.showError('secret_key', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
</div>
- </form>
- </ng-container>
-</cd-modal>
+
+ </div>
+
+ <cd-form-button-panel (submitActionEvent)="onSubmit()"
+ [form]="formGroup"
+ [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"
+ [showSubmit]="!viewing"
+ [modalForm]="true"></cd-form-button-panel>
+ </form>
+</cds-modal>
import { Component, EventEmitter, Output } from '@angular/core';
import { Validators } from '@angular/forms';
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import _ from 'lodash';
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { CdValidators } from '~/app/shared/forms/cd-validators';
import { RgwUserS3Key } from '../models/rgw-user-s3-key';
+import { BaseModal } from 'carbon-components-angular';
@Component({
selector: 'cd-rgw-user-s3-key-modal',
templateUrl: './rgw-user-s3-key-modal.component.html',
styleUrls: ['./rgw-user-s3-key-modal.component.scss']
})
-export class RgwUserS3KeyModalComponent {
+export class RgwUserS3KeyModalComponent extends BaseModal {
/**
* The event that is triggered when the 'Add' button as been pressed.
*/
resource: string;
action: string;
- constructor(
- private formBuilder: CdFormBuilder,
- public activeModal: NgbActiveModal,
- public actionLabels: ActionLabelsI18n
- ) {
+ constructor(private formBuilder: CdFormBuilder, public actionLabels: ActionLabelsI18n) {
+ super();
this.resource = $localize`S3 Key`;
this.createForm();
}
onSubmit() {
const key: RgwUserS3Key = this.formGroup.value;
this.submitAction.emit(key);
- this.activeModal.close();
+ this.closeModal();
}
}
-<cd-modal [modalRef]="bsModalRef">
- <ng-container i18n="form title"
- class="modal-title">{{ action | titlecase }} {{ resource | upperFirst }}</ng-container>
- <ng-container class="modal-content">
- <form #frm="ngForm"
- [formGroup]="formGroup"
- novalidate>
- <div class="modal-body">
+<cds-modal size="md"
+ [open]="open"
+ [hasScrollingContent]="false"
+ (overlaySelected)="closeModal()">
+ <cds-modal-header (closeSelect)="closeModal()">
+ <h3 cdsModalHeaderHeading
+ i18n>{{ action | titlecase }} {{ resource | upperFirst }}</h3>
- <!-- Username -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- for="uid"
- i18n>Username</label>
- <div class="cd-col-form-input">
- <input id="uid"
- class="form-control"
- type="text"
- formControlName="uid"
- [readonly]="true">
- </div>
- </div>
+ <cd-help-text [formAllFieldsRequired]="true"></cd-help-text>
+ </cds-modal-header>
- <!-- Subuser -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- [ngClass]="{'required': !editing}"
- for="subuid"
- i18n>Subuser</label>
- <div class="cd-col-form-input">
- <input id="subuid"
- class="form-control"
- type="text"
- formControlName="subuid"
- [readonly]="editing"
- autofocus>
- <span class="invalid-feedback"
- *ngIf="formGroup.showError('subuid', frm, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="formGroup.showError('subuid', frm, 'subuserIdExists')"
- i18n>The chosen subuser ID is already in use.</span>
- </div>
- </div>
+ <form #frm="ngForm"
+ [formGroup]="formGroup"
+ novalidate>
+ <div cdsModalContent>
+ <!-- Username -->
+ <div class="form-item">
+ <cds-text-label for="uid"
+ i18n>Username
+ <input cdsText
+ id="uid"
+ name="uid"
+ formControlName="uid"
+ readonly
+ modal-primary-focus>
+ </cds-text-label>
+ </div>
- <!-- Permission -->
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="perm"
- i18n>Permission</label>
- <div class="cd-col-form-input">
- <select id="perm"
- class="form-select"
- formControlName="perm">
- <option i18n
- [ngValue]="null">-- Select a permission --</option>
- <option *ngFor="let perm of ['read', 'write']"
- [value]="perm">
- {{ perm }}
- </option>
- <option i18n
- value="read-write">read, write</option>
- <option i18n
- value="full-control">full</option>
- </select>
- <span class="invalid-feedback"
- *ngIf="formGroup.showError('perm', frm, 'required')"
- i18n>This field is required.</span>
- </div>
+ <!-- Subuser -->
+ <div class="form-item">
+ <cds-text-label for="subuid"
+ [invalid]="formGroup.controls.subuid.invalid && formGroup.controls.subuid.dirty"
+ [invalidText]="subuserHelper">Subuser
+ <input cdsText
+ id="subuid"
+ name="subuid"
+ formControlName="subuid"
+ [readonly]="editing"
+ autofocus>
+ </cds-text-label>
+ <ng-template #subuserHelper>
+ <span class="invalid-feedback"
+ *ngIf="formGroup.showError('subuid', frm, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="formGroup.showError('subuid', frm, 'subuserIdExists')"
+ i18n>The chosen subuser ID is already in use.</span>
+ </ng-template>
+ </div>
+
+ <!-- Permission -->
+ <div class="form-item">
+ <cds-select label="Permission"
+ i18n-label
+ for="perm"
+ formControlName="perm"
+ [invalid]="formGroup.controls.perm.invalid && formGroup.controls.perm.dirty"
+ [invalidText]="permError">
+ <option value="">--- Select a permission ---</option>
+ <option *ngFor="let perm of ['read', 'write']"
+ [value]="perm">
+ {{ perm }}
+ </option>
+ <option i18n
+ value="read-write">read, write</option>
+ <option i18n
+ value="full-control">full</option>
+ </cds-select>
+
+ <ng-template #permError>
+ <span class="invalid-feedback"
+ *ngIf="formGroup.showError('perm', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
+ </div>
+
+ <!-- Swift key -->
+ <fieldset *ngIf="!editing">
+ <legend i18n>Swift key</legend>
+
+ <!-- Auto-generate key -->
+ <div class="form-item">
+ <cds-checkbox id="generate_secret"
+ formControlName="generate_secret"
+ i18n>Auto-generate key
+ </cds-checkbox>
</div>
- <!-- Swift key -->
- <fieldset *ngIf="!editing">
- <legend i18n>Swift key</legend>
+ <!-- Secret key -->
+ <div class="form-item form-item-append"
+ *ngIf="!editing && !formGroup.getValue('generate_secret')">
+ <cds-password-label for="secret_key"
+ [invalid]="formGroup.controls.secret_key.invalid && formGroup.controls.secret_key.dirty"
+ [invalidText]="secretKeyError"
+ i18n>Secret key
+ <input cdsPassword
+ id="secret_key"
+ name="secret_key"
+ formControlName="secret_key"
+ [invalid]="formGroup.controls.secret_key.invalid && formGroup.controls.secret_key.dirty"
+ [autofocus]="true">
+ </cds-password-label>
- <!-- Auto-generate key -->
- <div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="generate_secret"
- type="checkbox"
- formControlName="generate_secret">
- <label class="custom-control-label"
- for="generate_secret"
- i18n>Auto-generate secret</label>
- </div>
- </div>
- </div>
+ <cd-copy-2-clipboard-button source="secret_key"
+ class="mt-4">
+ </cd-copy-2-clipboard-button>
- <!-- Secret key -->
- <div class="form-group row"
- *ngIf="!editing && !formGroup.getValue('generate_secret')">
- <label class="cd-col-form-label required"
- for="secret_key"
- i18n>Secret key</label>
- <div class="cd-col-form-input">
- <div class="input-group">
- <input id="secret_key"
- class="form-control"
- type="password"
- formControlName="secret_key">
- <button type="button"
- class="btn btn-light"
- cdPasswordButton="secret_key">
- </button>
- <cd-copy-2-clipboard-button source="secret_key">
- </cd-copy-2-clipboard-button>
- </div>
- <span class="invalid-feedback"
- *ngIf="formGroup.showError('secret_key', frm, 'required')"
- i18n>This field is required.</span>
- </div>
- </div>
+ <ng-template #secretKeyError>
+ <span class="invalid-feedback"
+ *ngIf="formGroup.showError('secret_key', frm, 'required')"
+ i18n>This field is required.</span>
+ </ng-template>
+ </div>
- </fieldset>
+ </fieldset>
+ </div>
+ <cd-form-button-panel (submitActionEvent)="onSubmit()"
+ [form]="formGroup"
+ [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"
+ [modalForm]="true"></cd-form-button-panel>
- </div>
- <div class="modal-footer">
- <cd-form-button-panel (submitActionEvent)="onSubmit()"
- [form]="formGroup"
- [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"></cd-form-button-panel>
- </div>
- </form>
- </ng-container>
-</cd-modal>
+ </form>
+</cds-modal>
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
-
import { SharedModule } from '~/app/shared/shared.module';
import { configureTestBed } from '~/testing/unit-test-helper';
import { RgwUserSubuserModalComponent } from './rgw-user-subuser-modal.component';
+import { SelectModule } from 'carbon-components-angular';
describe('RgwUserSubuserModalComponent', () => {
let component: RgwUserSubuserModalComponent;
configureTestBed({
declarations: [RgwUserSubuserModalComponent],
- imports: [ReactiveFormsModule, SharedModule, RouterTestingModule],
- providers: [NgbActiveModal]
+ imports: [ReactiveFormsModule, SharedModule, RouterTestingModule, SelectModule]
});
beforeEach(() => {
import { Component, EventEmitter, Output } from '@angular/core';
import { AbstractControl, ValidationErrors, ValidatorFn, Validators } from '@angular/forms';
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import _ from 'lodash';
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { CdValidators, isEmptyInputValue } from '~/app/shared/forms/cd-validators';
import { RgwUserSubuser } from '../models/rgw-user-subuser';
+import { BaseModal } from 'carbon-components-angular';
@Component({
selector: 'cd-rgw-user-subuser-modal',
templateUrl: './rgw-user-subuser-modal.component.html',
styleUrls: ['./rgw-user-subuser-modal.component.scss']
})
-export class RgwUserSubuserModalComponent {
+export class RgwUserSubuserModalComponent extends BaseModal {
/**
* The event that is triggered when the 'Add' or 'Update' button
* has been pressed.
resource: string;
action: string;
- constructor(
- private formBuilder: CdFormBuilder,
- public bsModalRef: NgbActiveModal,
- private actionLabels: ActionLabelsI18n
- ) {
+ constructor(private formBuilder: CdFormBuilder, private actionLabels: ActionLabelsI18n) {
+ super();
this.resource = $localize`Subuser`;
this.createForm();
}
this.formGroup = this.formBuilder.group({
uid: [null],
subuid: [null, [Validators.required, this.subuserValidator()]],
- perm: [null, [Validators.required]],
+ perm: ['full-control', [Validators.required]],
// Swift key
generate_secret: [true],
secret_key: [null, [CdValidators.requiredIf({ generate_secret: false })]]
subuser.generate_secret = values.generate_secret;
subuser.secret_key = values.secret_key;
this.submitAction.emit(subuser);
- this.bsModalRef.close();
+ this.closeModal();
}
}
-<cd-modal [modalRef]="activeModal">
- <ng-container i18n="form title"
- class="modal-title">{{ action | titlecase }} {{ resource | upperFirst }}</ng-container>
+<cds-modal size="md"
+ [open]="open"
+ [hasScrollingContent]="false"
+ (overlaySelected)="closeModal()">
+ <cds-modal-header (closeSelect)="closeModal()">
+ <h3 cdsModalHeaderHeading
+ i18n>{{ action | titlecase }} {{ resource | upperFirst }}</h3>
+ </cds-modal-header>
- <ng-container class="modal-content">
- <div class="modal-body">
- <form novalidate>
- <!-- Username -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- for="user"
- i18n>Username</label>
- <div class="cd-col-form-input">
- <input id="user"
- name="user"
- class="form-control"
- type="text"
- [readonly]="true"
- [(ngModel)]="user">
- </div>
- </div>
+ <form novalidate>
+ <div cdsModalContent>
+ <!-- Username -->
+ <div class="form-item">
+ <cds-text-label for="user"
+ i18n>Username
+ <input cdsText
+ id="user"
+ name="user"
+ readonly
+ [(ngModel)]="user"
+ modal-primary-focus>
+ </cds-text-label>
+ </div>
- <!-- Secret key -->
- <div class="form-group row">
- <label class="cd-col-form-label"
- for="secret_key"
- i18n>Secret key</label>
- <div class="cd-col-form-input">
- <div class="input-group">
- <input id="secret_key"
- name="secret_key"
- class="form-control"
- type="password"
- [(ngModel)]="secret_key"
- [readonly]="true">
- <button type="button"
- class="btn btn-light"
- cdPasswordButton="secret_key">
- </button>
- <cd-copy-2-clipboard-button source="secret_key">
- </cd-copy-2-clipboard-button>
- </div>
- </div>
- </div>
- </form>
+ <!-- Secret key -->
+ <div class="form-item form-item-append">
+ <cds-password-label for="secret_key"
+ i18n>Secret key
+ <input cdsPassword
+ id="secret_key"
+ name="secret_key"
+ [(ngModel)]="secret_key"
+ readonly>
+ </cds-password-label>
+ <cd-copy-2-clipboard-button source="secret_key"
+ class="mt-4">
+ </cd-copy-2-clipboard-button>
+ </div>
</div>
-
- <div class="modal-footer">
- <cd-back-button (backAction)="activeModal.close()"></cd-back-button>
- </div>
- </ng-container>
-</cd-modal>
+ <cd-form-button-panel [showSubmit]="false"
+ (cancel)="closeModal()"
+ [modalForm]="true"></cd-form-button-panel>
+ </form>
+</cds-modal>
import { Component } from '@angular/core';
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { BaseModal } from 'carbon-components-angular';
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
templateUrl: './rgw-user-swift-key-modal.component.html',
styleUrls: ['./rgw-user-swift-key-modal.component.scss']
})
-export class RgwUserSwiftKeyModalComponent {
+export class RgwUserSwiftKeyModalComponent extends BaseModal {
user: string;
secret_key: string;
resource: string;
action: string;
- constructor(public activeModal: NgbActiveModal, public actionLabels: ActionLabelsI18n) {
+ constructor(public actionLabels: ActionLabelsI18n) {
+ super();
this.resource = $localize`Swift Key`;
this.action = this.actionLabels.SHOW;
}
TabsModule,
AccordionModule,
TagModule,
- TooltipModule
+ TooltipModule,
+ ComboBoxModule
} from 'carbon-components-angular';
import { CephSharedModule } from '../shared/ceph-shared.module';
import { RgwUserAccountsComponent } from './rgw-user-accounts/rgw-user-accounts.component';
SelectModule,
NumberModule,
TabsModule,
- IconModule,
- SelectModule,
RadioModule,
- SelectModule,
- NumberModule,
TagModule,
- TooltipModule
+ TooltipModule,
+ ComboBoxModule
],
exports: [
RgwDaemonListComponent,
// Icons
import InfoIcon from '@carbon/icons/es/information/16';
+import CopyIcon from '@carbon/icons/es/copy/32';
@NgModule({
imports: [
})
export class ComponentsModule {
constructor(private iconService: IconService) {
- this.iconService.registerAll([InfoIcon]);
+ this.iconService.registerAll([InfoIcon, CopyIcon]);
}
}
*ngIf="showIconOnly; else withButtonTpl"></i>
<ng-template #withButtonTpl>
- <button (click)="onClick()"
- type="button"
- class="btn btn-light"
- i18n-title
- title="Copy to Clipboard">
- <i [ngClass]="[icons.clipboard]"></i>
- </button>
+
+ <cds-icon-button kind="tertiary"
+ size="md"
+ title="Copy to Clipboard"
+ (click)="onClick()">
+ <svg cdsIcon="copy"
+ size="32"
+ class="cds--btn__icon"></svg>
+ </cds-icon-button>
</ng-template>
<div class="form-text text-muted">
- <ng-content></ng-content>
+ <ng-container *ngIf="formAllFieldsRequired; else contentTpl"
+ i18n>
+ All fields are required, except where marked optional.
+ </ng-container>
+
+ <ng-template #contentTpl>
+ <ng-content></ng-content>
+ </ng-template>
</div>
-import { Component } from '@angular/core';
+import { Component, Input } from '@angular/core';
@Component({
selector: 'cd-help-text',
templateUrl: './help-text.component.html',
styleUrls: ['./help-text.component.scss']
})
-export class HelpTextComponent {}
+export class HelpTextComponent {
+ @Input()
+ formAllFieldsRequired = false;
+}