[CST-4506] Address feedback

This commit is contained in:
Giuseppe Digilio
2022-01-24 19:54:21 +01:00
parent 532d8fb9a3
commit 973ceb3b4b
13 changed files with 115 additions and 160 deletions

View File

@@ -1,7 +1,5 @@
import { autoserialize } from 'cerialize';
/**
* Model class for an Access Condition
* Model class for an Item Access Condition
*/
export class AccessesConditionOption {
@@ -15,7 +13,6 @@ export class AccessesConditionOption {
*/
groupName: string;
/**
* A boolean representing if this Access Condition has a start date
*/

View File

@@ -1,27 +1,39 @@
import { autoserialize, inheritSerialization, deserialize } from 'cerialize';
import { typedObject, link } from '../../cache/builders/build-decorators';
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
import { typedObject } from '../../cache/builders/build-decorators';
import { ConfigObject } from './config.model';
import { AccessesConditionOption } from './config-accesses-conditions-options.model';
import { SUBMISSION_ACCESSES_TYPE } from './config-type';
import { HALLink } from '../../shared/hal-link.model';
/**
* Class for the configuration describing the item accesses condition
*/
@typedObject
@inheritSerialization(ConfigObject)
export class SubmissionAccessModel extends ConfigObject {
static type = SUBMISSION_ACCESSES_TYPE;
/**
* A list of available bitstream access conditions
* A list of available item access conditions
*/
@autoserialize
accessConditionOptions: AccessesConditionOption[];
/**
* Boolean that indicates whether the current item must be findable via search or browse.
*/
@autoserialize
discoverable: boolean;
/**
* Boolean that indicates whether or not the user can change the discoverable flag.
*/
@autoserialize
canChangeDiscoverable: boolean;
/**
* The links to all related resources returned by the rest api.
*/
@deserialize
_links: {
self: HALLink

View File

@@ -0,0 +1,25 @@
/**
* An interface to represent an access condition.
*/
export class AccessConditionObject {
/**
* The access condition id
*/
id: string;
/**
* The access condition name
*/
name: string;
/**
* Possible start date of the access condition
*/
startDate: string;
/**
* Possible end date of the access condition
*/
endDate: string;
}

View File

@@ -1,37 +0,0 @@
import { autoserialize, inheritSerialization } from 'cerialize';
import { typedObject } from '../../cache/builders/build-decorators';
import { excludeFromEquals } from '../../utilities/equals.decorators';
import { ResourceType } from '../../shared/resource-type';
import { HALResource } from '../../shared/hal-resource.model';
import { SUBMISSION_ACCESSES } from './submission-accesses.resource-type';
@typedObject
@inheritSerialization(HALResource)
export class SubmissionAccesses extends HALResource {
static type = SUBMISSION_ACCESSES;
/**
* The object type
*/
@excludeFromEquals
@autoserialize
type: ResourceType;
@autoserialize
discoverable: boolean;
@autoserialize
accessConditions: AccessConditions[];
}
export interface AccessConditions {
name: string;
startDate?: Date;
hasStartDate?: boolean;
maxStartDate?: string;
hasEndDate?: boolean;
maxEndDate?: string;
endDate?: Date;
}

View File

@@ -1,7 +1,7 @@
import { ResourceType } from '../../shared/resource-type';
/**
* The resource type for License
* The resource type for Accesses section
*
* Needs to be in a separate file to prevent circular
* dependencies in webpack.

View File

@@ -0,0 +1,8 @@
import { AccessConditionObject } from './access-condition.model';
/**
* An interface to represent item's access condition.
*/
export class SubmissionItemAccessConditionObject extends AccessConditionObject {
}

View File

@@ -1,25 +1,8 @@
import { AccessConditionObject } from './access-condition.model';
/**
* An interface to represent bitstream's access condition.
*/
export class SubmissionUploadFileAccessConditionObject {
export class SubmissionUploadFileAccessConditionObject extends AccessConditionObject {
/**
* The access condition id
*/
id: string;
/**
* The access condition name
*/
name: string;
/**
* Possible start date of the access condition
*/
startDate: string;
/**
* Possible end date of the access condition
*/
endDate: string;
}

View File

@@ -1,18 +1,21 @@
import { SubmissionItemAccessConditionObject } from './submission-item-access-condition.model';
/**
* An interface to represent the submission's creative commons license section data.
* An interface to represent the submission's item accesses condition.
*/
export interface WorkspaceitemSectionAccessesObject {
/**
* The access condition id
*/
id: string;
/**
* Boolean that indicates whether the current item must be findable via search or browse.
*/
discoverable: boolean;
accessConditions: [
{
name: string;
startDate?: Date;
hasStartDate?: boolean;
maxStartDate?: string;
hasEndDate?: boolean;
maxEndDate?: string;
endDate?: Date;
}
];
/**
* A list of available item access conditions
*/
accessConditions: SubmissionItemAccessConditionObject[];
}

View File

@@ -14,7 +14,8 @@
<div>
<ng-container #componentViewContainer></ng-container>
</div>
<small *ngIf="hasHint && ((model.repeatable === false && (isRelationship === false || value?.value === null)) || (model.repeatable === true && context?.index === context?.context?.groups?.length - 1)) && (!showErrorMessages || errorMessages.length === 0)"
<small *ngIf="hasHint && ((!model.repeatable && (isRelationship === false || value?.value === null)) || (model.repeatable === true && context?.index === context?.context?.groups?.length - 1)) && (!showErrorMessages || errorMessages.length === 0)"
class="text-muted ds-hint" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
<!-- In case of repeatable fields show empty space for all elements except the first -->
<div *ngIf="context?.index !== null

View File

@@ -1,11 +1,15 @@
import { SectionAccessesService } from './section-accesses.service';
import { Component, Inject, ViewChild } from '@angular/core';
import { filter, map, mergeMap, take } from 'rxjs/operators';
import { combineLatest, Observable, of, Subscription } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
import { renderSectionFor } from '../sections-decorator';
import { SectionsType } from '../sections-type';
import { SectionDataObject } from '../models/section-data.model';
import { SectionsService } from '../sections.service';
import { SectionModelComponent } from '../models/section.model';
import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs';
import {
DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX,
DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER,
@@ -19,13 +23,15 @@ import {
MATCH_ENABLED,
OR_OPERATOR
} from '@ng-dynamic-forms/core';
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
import {
ACCESS_CONDITION_GROUP_CONFIG,
ACCESS_CONDITION_GROUP_LAYOUT,
ACCESS_CONDITIONS_FORM_ARRAY_CONFIG,
ACCESS_CONDITIONS_FORM_ARRAY_LAYOUT,
ACCESS_FORM_CHECKBOX_CONFIG,
ACCESS_FORM_CHECKBOX_LAYOUT,
FORM_ACCESS_CONDITION_END_DATE_CONFIG,
FORM_ACCESS_CONDITION_END_DATE_LAYOUT,
FORM_ACCESS_CONDITION_START_DATE_CONFIG,
@@ -37,17 +43,18 @@ import { hasValue, isNotEmpty, isNotNull } from '../../../shared/empty.util';
import { WorkspaceitemSectionAccessesObject } from '../../../core/submission/models/workspaceitem-section-accesses.model';
import { SubmissionAccessesConfigService } from '../../../core/config/submission-accesses-config.service';
import { getFirstSucceededRemoteData } from '../../../core/shared/operators';
import { filter, map, mergeMap, take } from 'rxjs/operators';
import { FormComponent } from '../../../shared/form/form.component';
import { FormService } from '../../../shared/form/form.service';
import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner';
import { SectionFormOperationsService } from '../form/section-form-operations.service';
import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder';
import { AccessesConditionOption } from '../../../core/config/models/config-accesses-conditions-options.model';
import { TranslateService } from '@ngx-translate/core';
import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
import { dateToISOFormat } from '../../../shared/date.util';
/**
* This component represents a section for managing item's access conditions.
*/
@Component({
selector: 'ds-section-accesses',
templateUrl: './section-accesses.component.html',
@@ -62,7 +69,7 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent {
@ViewChild('formRef') public formRef: FormComponent;
/**
* List of available access conditions that could be set to files
* List of available access conditions that could be set to item
*/
public availableAccessConditionOptions: AccessesConditionOption[]; // List of accessConditions that an user can select
@@ -73,52 +80,34 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent {
public formId: string;
/**
* The accesses metadata data
* The accesses section data
* @type {WorkspaceitemSectionAccessesObject}
*/
public accessesData: WorkspaceitemSectionAccessesObject;
/**
* The collection name this submission belonging to
* @type {string}
*/
public collectionName: string;
/**
* Is the upload required
* @type {boolean}
*/
public required$ = new BehaviorSubject<boolean>(true);
/**
* The form model
* @type {DynamicFormControlModel[]}
*/
formModel: DynamicFormControlModel[];
public formModel: DynamicFormControlModel[];
/**
* Array to track all subscriptions and unsubscribe them onDestroy
* @type {Array}
*/
protected subs: Subscription[] = [];
/**
* The [[JsonPatchOperationPathCombiner]] object
* @type {JsonPatchOperationPathCombiner}
*/
protected pathCombiner: JsonPatchOperationPathCombiner;
/**
* A map representing all field prevous values
* @type {Map}
*/
protected previousValue: Map<string, number[]> = new Map();
/**
* A map representing all field on their way to be removed
* @type {Map}
*/
protected fieldsOnTheirWayToBeRemoved: Map<string, number[]> = new Map();
/**
* Defines if the access discoverable property can be managed
*/
public canChangeDiscoverable: boolean;
/**
* Initialize instance variables
*
@@ -233,36 +222,6 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent {
}
}
/**
* Check if the specified form field has already a value stored
*
* @param fieldId
* the section data retrieved from the serverù
* @param index
* the section data retrieved from the server
*/
hasStoredValue(fieldId, index): boolean {
if (isNotEmpty(this.sectionData.data)) {
return this.sectionData.data.hasOwnProperty(fieldId) &&
isNotEmpty(this.sectionData.data[fieldId][index]) &&
!this.isFieldToRemove(fieldId, index);
} else {
return false;
}
}
/**
* Check if the specified field is on the way to be removed
*
* @param fieldId
* the section data retrieved from the serverù
* @param index
* the section data retrieved from the server
*/
isFieldToRemove(fieldId, index) {
return this.fieldsOnTheirWayToBeRemoved.has(fieldId) && this.fieldsOnTheirWayToBeRemoved.get(fieldId).includes(index);
}
/**
* Unsubscribe from all subscriptions
*/
@@ -286,7 +245,7 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent {
const accessData$ = this.accessesService.getAccessesData(this.submissionId, this.sectionData.id);
combineLatest(config$, accessData$).subscribe(([config, accessData]) => {
combineLatest([config$, accessData$]).subscribe(([config, accessData]) => {
this.availableAccessConditionOptions = isNotEmpty(config.accessConditionOptions) ? config.accessConditionOptions : [];
this.canChangeDiscoverable = !!config.canChangeDiscoverable;
this.accessesData = accessData;
@@ -313,17 +272,16 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent {
const formModel: DynamicFormControlModel[] = [];
if (this.canChangeDiscoverable) {
formModel.push(
new DynamicCheckboxModel({
id: 'discoverable',
const discoverableCheckboxConfig = Object.assign({}, ACCESS_FORM_CHECKBOX_CONFIG, {
label: this.translate.instant('submission.sections.accesses.form.discoverable-label'),
name: 'discoverable',
hint: this.translate.instant('submission.sections.accesses.form.discoverable-description'),
value: this.accessesData.discoverable
})
});
formModel.push(
new DynamicCheckboxModel(discoverableCheckboxConfig, ACCESS_FORM_CHECKBOX_LAYOUT)
);
}
const accessConditionTypeModelConfig = Object.assign({}, FORM_ACCESS_CONDITION_TYPE_CONFIG);
const accessConditionsArrayConfig = Object.assign({}, ACCESS_CONDITIONS_FORM_ARRAY_CONFIG);
const accessConditionTypeOptions = [];

View File

@@ -7,19 +7,22 @@ import {
MATCH_ENABLED,
OR_OPERATOR,
} from '@ng-dynamic-forms/core';
import { DynamicCheckboxModelConfig } from '@ng-dynamic-forms/core/lib/model/checkbox/dynamic-checkbox.model';
export const ACCESS_FORM_CHECKBOX_LAYOUT: DynamicFormControlLayout = {
element: {
host: 'form-group flex-fill access-condition-group',
export const ACCESS_FORM_CHECKBOX_CONFIG: DynamicCheckboxModelConfig = {
id: 'discoverable',
// disabled: false,
label: 'submission.sections.accesses.form.discoverable-label',
name: 'discoverable',
}
name: 'discoverable'
};
export const ACCESS_FORM_CHECKBOX_LAYOUT = {
element: {
container: 'custom-control custom-checkbox pl-1',
control: 'custom-control-input',
label: 'custom-control-label pt-1'
}
};
export const ACCESS_CONDITION_GROUP_CONFIG: DynamicFormGroupModelConfig = {
id: 'accessConditionGroup',
@@ -28,7 +31,7 @@ export const ACCESS_CONDITION_GROUP_CONFIG: DynamicFormGroupModelConfig = {
export const ACCESS_CONDITION_GROUP_LAYOUT: DynamicFormControlLayout = {
element: {
host: 'form-group flex-fill access-condition-group',
host: 'form-group flex-fill',
container: 'pl-1 pr-1',
control: 'form-row '
}

View File

@@ -5,12 +5,12 @@ import { distinctUntilChanged, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { SubmissionState } from '../../submission.reducers';
import { isUndefined } from 'util';
import { isNotUndefined } from '../../../shared/empty.util';
import { submissionSectionDataFromIdSelector } from '../../selectors';
import { WorkspaceitemSectionAccessesObject } from '../../../core/submission/models/workspaceitem-section-accesses.model';
/**
* A service that provides methods to handle submission's bitstream state.
* A service that provides methods to handle submission item's accesses condition state.
*/
@Injectable()
export class SectionAccessesService {
@@ -24,7 +24,7 @@ export class SectionAccessesService {
/**
* Return bitstream's metadata
* Return item's accesses condition state.
*
* @param submissionId
* The submission id
@@ -33,10 +33,10 @@ export class SectionAccessesService {
* @returns {Observable}
* Emits bitstream's metadata
*/
public getAccessesData(submissionId: string, sectionId: string): Observable<any> {
public getAccessesData(submissionId: string, sectionId: string): Observable<WorkspaceitemSectionAccessesObject> {
return this.store.select(submissionSectionDataFromIdSelector(submissionId, sectionId)).pipe(
filter((state) => !isUndefined(state)),
filter((state) => isNotUndefined(state)),
distinctUntilChanged());
}
}

View File

@@ -3895,6 +3895,8 @@
"submission.sections.upload.upload-successful": "Upload successful",
"submission.sections.accesses.form.discoverable-description": "When checked, this item will be discoverable in search/browse. When unchecked, the item will only be available via a direct link and will never appear in search/browse.",
"submission.sections.accesses.form.discoverable-label": "Discoverable",