forked from hazza/dspace-angular
Merge pull request #1475 from 4Science/CST-4506_item_embargo
Add submission section for item embargo
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Model class for an Item Access Condition
|
||||
*/
|
||||
export class AccessesConditionOption {
|
||||
|
||||
/**
|
||||
* The name for this Access Condition
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* The groupName for this Access Condition
|
||||
*/
|
||||
groupName: string;
|
||||
|
||||
/**
|
||||
* A boolean representing if this Access Condition has a start date
|
||||
*/
|
||||
hasStartDate: boolean;
|
||||
|
||||
/**
|
||||
* A boolean representing if this Access Condition has an end date
|
||||
*/
|
||||
hasEndDate: boolean;
|
||||
|
||||
/**
|
||||
* Maximum value of the start date
|
||||
*/
|
||||
endDateLimit?: string;
|
||||
|
||||
/**
|
||||
* Maximum value of the end date
|
||||
*/
|
||||
startDateLimit?: string;
|
||||
|
||||
/**
|
||||
* Maximum value of the start date
|
||||
*/
|
||||
maxStartDate?: string;
|
||||
|
||||
/**
|
||||
* Maximum value of the end date
|
||||
*/
|
||||
maxEndDate?: string;
|
||||
}
|
42
src/app/core/config/models/config-submission-access.model.ts
Normal file
42
src/app/core/config/models/config-submission-access.model.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
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 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
|
||||
};
|
||||
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
import { inheritSerialization } from 'cerialize';
|
||||
import { typedObject } from '../../cache/builders/build-decorators';
|
||||
import { SUBMISSION_ACCESSES_TYPE } from './config-type';
|
||||
import { SubmissionAccessModel } from './config-submission-access.model';
|
||||
|
||||
@typedObject
|
||||
@inheritSerialization(SubmissionAccessModel)
|
||||
export class SubmissionAccessesModel extends SubmissionAccessModel {
|
||||
static type = SUBMISSION_ACCESSES_TYPE;
|
||||
}
|
@@ -15,3 +15,5 @@ export const SUBMISSION_SECTION_TYPE = new ResourceType('submissionsection');
|
||||
export const SUBMISSION_UPLOADS_TYPE = new ResourceType('submissionuploads');
|
||||
|
||||
export const SUBMISSION_UPLOAD_TYPE = new ResourceType('submissionupload');
|
||||
|
||||
export const SUBMISSION_ACCESSES_TYPE = new ResourceType('submissionaccessoption');
|
||||
|
42
src/app/core/config/submission-accesses-config.service.ts
Normal file
42
src/app/core/config/submission-accesses-config.service.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ConfigService } from './config.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { SUBMISSION_ACCESSES_TYPE } from './models/config-type';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
|
||||
import { ConfigObject } from './models/config.model';
|
||||
import { SubmissionAccessesModel } from './models/config-submission-accesses.model';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { Observable } from 'rxjs';
|
||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||
|
||||
/**
|
||||
* Provides methods to retrieve, from REST server, bitstream access conditions configurations applicable during the submission process.
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(SUBMISSION_ACCESSES_TYPE)
|
||||
export class SubmissionAccessesConfigService extends ConfigService {
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected comparator: DefaultChangeAnalyzer<SubmissionAccessesModel>
|
||||
) {
|
||||
super(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator, 'submissionaccessoptions');
|
||||
}
|
||||
|
||||
findByHref(href: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow): Observable<RemoteData<SubmissionAccessesModel>> {
|
||||
return super.findByHref(href, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow as FollowLinkConfig<ConfigObject>[]) as Observable<RemoteData<SubmissionAccessesModel>>;
|
||||
}
|
||||
}
|
@@ -2,11 +2,7 @@ import { CommonModule } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
|
||||
|
||||
import {
|
||||
DynamicFormLayoutService,
|
||||
DynamicFormService,
|
||||
DynamicFormValidationService
|
||||
} from '@ng-dynamic-forms/core';
|
||||
import { DynamicFormLayoutService, DynamicFormService, DynamicFormValidationService } from '@ng-dynamic-forms/core';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
|
||||
import { Action, StoreConfig, StoreModule } from '@ngrx/store';
|
||||
@@ -165,6 +161,7 @@ import { Root } from './data/root.model';
|
||||
import { SearchConfig } from './shared/search/search-filters/search-config.model';
|
||||
import { SequenceService } from './shared/sequence.service';
|
||||
import { GroupDataService } from './eperson/group-data.service';
|
||||
import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model';
|
||||
|
||||
/**
|
||||
* When not in production, endpoint responses can be mocked for testing purposes
|
||||
@@ -347,7 +344,8 @@ export const models =
|
||||
Registration,
|
||||
UsageReport,
|
||||
Root,
|
||||
SearchConfig
|
||||
SearchConfig,
|
||||
SubmissionAccessesModel
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
25
src/app/core/submission/models/access-condition.model.ts
Normal file
25
src/app/core/submission/models/access-condition.model.ts
Normal 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;
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
import { ResourceType } from '../../shared/resource-type';
|
||||
|
||||
/**
|
||||
* The resource type for Accesses section
|
||||
*
|
||||
* Needs to be in a separate file to prevent circular
|
||||
* dependencies in webpack.
|
||||
*/
|
||||
export const SUBMISSION_ACCESSES = new ResourceType('submissionaccesses');
|
@@ -0,0 +1,8 @@
|
||||
import { AccessConditionObject } from './access-condition.model';
|
||||
|
||||
/**
|
||||
* An interface to represent item's access condition.
|
||||
*/
|
||||
export class SubmissionItemAccessConditionObject extends AccessConditionObject {
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
@@ -0,0 +1,21 @@
|
||||
import { SubmissionItemAccessConditionObject } from './submission-item-access-condition.model';
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A list of available item access conditions
|
||||
*/
|
||||
accessConditions: SubmissionItemAccessConditionObject[];
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
import { WorkspaceitemSectionAccessesObject } from './workspaceitem-section-accesses.model';
|
||||
import { WorkspaceitemSectionFormObject } from './workspaceitem-section-form.model';
|
||||
import { WorkspaceitemSectionLicenseObject } from './workspaceitem-section-license.model';
|
||||
import { WorkspaceitemSectionUploadObject } from './workspaceitem-section-upload.model';
|
||||
@@ -19,4 +20,5 @@ export type WorkspaceitemSectionDataType
|
||||
| WorkspaceitemSectionFormObject
|
||||
| WorkspaceitemSectionLicenseObject
|
||||
| WorkspaceitemSectionCcLicenseObject
|
||||
| WorkspaceitemSectionAccessesObject
|
||||
| string;
|
||||
|
@@ -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
|
||||
|
@@ -15,8 +15,8 @@
|
||||
[cdkDragDisabled]="dragDisabled"
|
||||
[cdkDragPreviewClass]="'ds-submission-reorder-dragging'">
|
||||
<!-- Item content -->
|
||||
<div class="drag-handle" [class.invisible]="dragDisabled" tabindex="0">
|
||||
<i class="drag-icon fas fa-grip-vertical fa-fw" ></i>
|
||||
<div class="drag-handle" [class.drag-disable]="dragDisabled" tabindex="0">
|
||||
<i class="drag-icon fas fa-grip-vertical fa-fw" [class.drag-disable]="dragDisabled" ></i>
|
||||
</div>
|
||||
<ng-container *ngTemplateOutlet="startTemplate?.templateRef; context: groupModel"></ng-container>
|
||||
<ds-dynamic-form-control-container *ngFor="let _model of groupModel.group"
|
||||
|
@@ -3,7 +3,15 @@
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.drag-disable {
|
||||
visibility: hidden !important;
|
||||
&:hover, &:focus {
|
||||
cursor: default;
|
||||
.drag-icon {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cdk-drag {
|
||||
margin-left: calc(-2.3 * var(--bs-spacer));
|
||||
margin-right: calc(-0.5 * var(--bs-spacer));
|
||||
|
87
src/app/shared/mocks/section-accesses-config.service.mock.ts
Normal file
87
src/app/shared/mocks/section-accesses-config.service.mock.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { SubmissionFormsConfigService } from '../../core/config/submission-forms-config.service';
|
||||
import { SubmissionFormsModel } from '../../core/config/models/config-submission-forms.model';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
|
||||
|
||||
const configRes = Object.assign(new SubmissionFormsModel(), {
|
||||
'id': 'AccessConditionDefaultConfiguration',
|
||||
'canChangeDiscoverable': true,
|
||||
'accessConditionOptions': [
|
||||
{
|
||||
'name': 'openaccess',
|
||||
'hasStartDate': false,
|
||||
'hasEndDate': false
|
||||
},
|
||||
{
|
||||
'name': 'lease',
|
||||
'hasStartDate': false,
|
||||
'hasEndDate': true,
|
||||
'maxEndDate': '2022-06-20T12:17:44.420+00:00'
|
||||
},
|
||||
{
|
||||
'name': 'embargo',
|
||||
'hasStartDate': true,
|
||||
'hasEndDate': false,
|
||||
'maxStartDate': '2024-12-20T12:17:44.420+00:00'
|
||||
},
|
||||
{
|
||||
'name': 'administrator',
|
||||
'hasStartDate': false,
|
||||
'hasEndDate': false
|
||||
}
|
||||
],
|
||||
'type': 'submissionaccessoption',
|
||||
'_links': {
|
||||
'self': {
|
||||
'href': 'http://localhost:8080/server/api/config/submissionaccessoptions/AccessConditionDefaultConfiguration'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const configResNotChangeDiscoverable = Object.assign(new SubmissionFormsModel(), {
|
||||
'id': 'AccessConditionDefaultConfiguration',
|
||||
'canChangeDiscoverable': false,
|
||||
'accessConditionOptions': [
|
||||
{
|
||||
'name': 'openaccess',
|
||||
'hasStartDate': false,
|
||||
'hasEndDate': false
|
||||
},
|
||||
{
|
||||
'name': 'lease',
|
||||
'hasStartDate': false,
|
||||
'hasEndDate': true,
|
||||
'maxEndDate': '2022-06-20T12:17:44.420+00:00'
|
||||
},
|
||||
{
|
||||
'name': 'embargo',
|
||||
'hasStartDate': true,
|
||||
'hasEndDate': false,
|
||||
'maxStartDate': '2024-12-20T12:17:44.420+00:00'
|
||||
},
|
||||
{
|
||||
'name': 'administrator',
|
||||
'hasStartDate': false,
|
||||
'hasEndDate': false
|
||||
}
|
||||
],
|
||||
'type': 'submissionaccessoption',
|
||||
'_links': {
|
||||
'self': {
|
||||
'href': 'http://localhost:8080/server/api/config/submissionaccessoptions/AccessConditionDefaultConfiguration'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export function getSubmissionAccessesConfigService(): SubmissionFormsConfigService {
|
||||
return jasmine.createSpyObj('SubmissionAccessesConfigService', {
|
||||
findByHref: createSuccessfulRemoteDataObject$(configRes),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function getSubmissionAccessesConfigNotChangeDiscoverableService(): SubmissionFormsConfigService {
|
||||
return jasmine.createSpyObj('SubmissionAccessesConfigService', {
|
||||
findByHref: createSuccessfulRemoteDataObject$(configResNotChangeDiscoverable),
|
||||
});
|
||||
}
|
13
src/app/shared/mocks/section-accesses.service.mock.ts
Normal file
13
src/app/shared/mocks/section-accesses.service.mock.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { SubmissionFormsModel } from '../../core/config/models/config-submission-forms.model';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
const dataRes = Object.assign(new SubmissionFormsModel(), {
|
||||
'id': 'AccessConditionDefaultConfiguration',
|
||||
'accessConditions': [],
|
||||
});
|
||||
|
||||
export function getSectionAccessesService() {
|
||||
return jasmine.createSpyObj('SectionAccessesService', {
|
||||
getAccessesData: observableOf(dataRes),
|
||||
});
|
||||
}
|
@@ -1723,3 +1723,94 @@ export const mockFileFormData = {
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
export const mockAccessesFormData = {
|
||||
discoverable: true,
|
||||
accessCondition: [
|
||||
{
|
||||
accessConditionGroup: {
|
||||
name: [
|
||||
{
|
||||
value: 'openaccess',
|
||||
language: null,
|
||||
authority: null,
|
||||
display: 'openaccess',
|
||||
confidence: -1,
|
||||
place: 0,
|
||||
otherInformation: null
|
||||
}
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
accessConditionGroup: {
|
||||
name: [
|
||||
{
|
||||
value: 'lease',
|
||||
language: null,
|
||||
authority: null,
|
||||
display: 'lease',
|
||||
confidence: -1,
|
||||
place: 0,
|
||||
otherInformation: null
|
||||
}
|
||||
],
|
||||
endDate: [
|
||||
{
|
||||
value: {
|
||||
year: 2019,
|
||||
month: 1,
|
||||
day: 16
|
||||
},
|
||||
language: null,
|
||||
authority: null,
|
||||
display: {
|
||||
year: 2019,
|
||||
month: 1,
|
||||
day: 16
|
||||
},
|
||||
confidence: -1,
|
||||
place: 0,
|
||||
otherInformation: null
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
{
|
||||
accessConditionGroup: {
|
||||
name: [
|
||||
{
|
||||
value: 'embargo',
|
||||
language: null,
|
||||
authority: null,
|
||||
display: 'lease',
|
||||
confidence: -1,
|
||||
place: 0,
|
||||
otherInformation: null
|
||||
}
|
||||
],
|
||||
startDate: [
|
||||
{
|
||||
value: {
|
||||
year: 2019,
|
||||
month: 1,
|
||||
day: 16
|
||||
},
|
||||
language: null,
|
||||
authority: null,
|
||||
display: {
|
||||
year: 2019,
|
||||
month: 1,
|
||||
day: 16
|
||||
},
|
||||
confidence: -1,
|
||||
place: 0,
|
||||
otherInformation: null
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
100
src/app/shared/testing/form-event.stub.ts
Normal file
100
src/app/shared/testing/form-event.stub.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { DynamicCheckboxModel, DynamicSelectModel } from '@ng-dynamic-forms/core';
|
||||
|
||||
export const accessConditionChangeEvent = {
|
||||
$event: {
|
||||
bubbles: true,
|
||||
cancelBubble: false,
|
||||
cancelable: false,
|
||||
composed: false,
|
||||
currentTarget: null,
|
||||
defaultPrevented: false,
|
||||
eventPhase: 0,
|
||||
isTrusted: true,
|
||||
returnValue: true,
|
||||
timeStamp: 143042.8999999999,
|
||||
type: 'change',
|
||||
},
|
||||
context: null,
|
||||
control: new FormControl({
|
||||
errors: null,
|
||||
pristine: false,
|
||||
status: 'VALID',
|
||||
statusChanges: { _isScalar: false, observers: [], closed: false, isStopped: false, hasError: false },
|
||||
touched: true,
|
||||
value: { year: 2021, month: 12, day: 30 },
|
||||
valueChanges: { _isScalar: false, observers: [], closed: false, isStopped: false, hasError: false },
|
||||
_updateOn: 'change',
|
||||
}),
|
||||
group: new FormGroup({}),
|
||||
model: new DynamicSelectModel({
|
||||
additional: null,
|
||||
asyncValidators: null,
|
||||
controlTooltip: null,
|
||||
errorMessages: { required: 'submission.sections.upload.form.date-required-until' },
|
||||
hidden: false,
|
||||
hint: null,
|
||||
id: 'endDate',
|
||||
label: 'submission.sections.upload.form.until-label',
|
||||
labelTooltip: null,
|
||||
name: 'endDate',
|
||||
placeholder: 'Until',
|
||||
prefix: null,
|
||||
relations: [],
|
||||
required: true,
|
||||
suffix: null,
|
||||
tabIndex: null,
|
||||
updateOn: null,
|
||||
validators: { required: null },
|
||||
}),
|
||||
type: 'change'
|
||||
};
|
||||
|
||||
|
||||
export const checkboxChangeEvent = {
|
||||
$event: {
|
||||
bubbles: true,
|
||||
cancelBubble: false,
|
||||
cancelable: false,
|
||||
composed: false,
|
||||
currentTarget: null,
|
||||
defaultPrevented: false,
|
||||
eventPhase: 0,
|
||||
isTrusted: true,
|
||||
returnValue: true,
|
||||
timeStamp: 143042.8999999999,
|
||||
type: 'change',
|
||||
},
|
||||
context: null,
|
||||
control: new FormControl({
|
||||
errors: null,
|
||||
pristine: false,
|
||||
status: 'VALID',
|
||||
statusChanges: { _isScalar: false, observers: [], closed: false, isStopped: false, hasError: false },
|
||||
touched: true,
|
||||
value: { year: 2021, month: 12, day: 30 },
|
||||
valueChanges: { _isScalar: false, observers: [], closed: false, isStopped: false, hasError: false },
|
||||
_updateOn: 'change',
|
||||
}),
|
||||
group: new FormGroup({}),
|
||||
model: new DynamicCheckboxModel({
|
||||
additional: null,
|
||||
asyncValidators: null,
|
||||
controlTooltip: null,
|
||||
errorMessages: null,
|
||||
hidden: false,
|
||||
hint: null,
|
||||
id: 'discoverable',
|
||||
indeterminate: false,
|
||||
label: 'Discoverable',
|
||||
labelPosition: null,
|
||||
labelTooltip: null,
|
||||
name: 'discoverable',
|
||||
relations: [],
|
||||
required: false,
|
||||
tabIndex: null,
|
||||
updateOn: null,
|
||||
validators: { required: null },
|
||||
}),
|
||||
type: 'change'
|
||||
};
|
@@ -0,0 +1,7 @@
|
||||
<ds-form *ngIf="!!formModel" #formRef="formComponent"
|
||||
[formId]="formId"
|
||||
[formModel]="formModel"
|
||||
[displaySubmit]="false"
|
||||
[displayCancel]="false"
|
||||
(dfChange)="onChange($event)"
|
||||
(removeArrayItem)="onRemove($event)"></ds-form>
|
@@ -0,0 +1,5 @@
|
||||
::ng-deep .access-condition-group {
|
||||
position: relative;
|
||||
top: -2.3rem;
|
||||
margin-bottom: -2.3rem;
|
||||
}
|
@@ -0,0 +1,204 @@
|
||||
import { FormService } from '../../../shared/form/form.service';
|
||||
import { ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SubmissionSectionAccessesComponent } from './section-accesses.component';
|
||||
import { SectionsService } from '../sections.service';
|
||||
import { SectionsServiceStub } from '../../../shared/testing/sections-service.stub';
|
||||
|
||||
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
|
||||
import { getMockFormBuilderService } from '../../../shared/mocks/form-builder-service.mock';
|
||||
import { SubmissionAccessesConfigService } from '../../../core/config/submission-accesses-config.service';
|
||||
import {
|
||||
getSubmissionAccessesConfigNotChangeDiscoverableService,
|
||||
getSubmissionAccessesConfigService
|
||||
} from '../../../shared/mocks/section-accesses-config.service.mock';
|
||||
import { SectionAccessesService } from './section-accesses.service';
|
||||
import { SectionFormOperationsService } from '../form/section-form-operations.service';
|
||||
import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
|
||||
import { getSectionAccessesService } from '../../../shared/mocks/section-accesses.service.mock';
|
||||
import { getMockFormOperationsService } from '../../../shared/mocks/form-operations-service.mock';
|
||||
import { getMockTranslateService } from '../../../shared/mocks/translate.service.mock';
|
||||
import { SubmissionJsonPatchOperationsServiceStub } from '../../../shared/testing/submission-json-patch-operations-service.stub';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { FormComponent } from '../../../shared/form/form.component';
|
||||
import {
|
||||
DynamicCheckboxModel,
|
||||
DynamicDatePickerModel,
|
||||
DynamicFormArrayModel,
|
||||
DynamicSelectModel
|
||||
} from '@ng-dynamic-forms/core';
|
||||
import { AppState } from '../../../app.reducer';
|
||||
import { getMockFormService } from '../../../shared/mocks/form-service.mock';
|
||||
import { mockAccessesFormData } from '../../../shared/mocks/submission.mock';
|
||||
import { accessConditionChangeEvent, checkboxChangeEvent } from '../../../shared/testing/form-event.stub';
|
||||
|
||||
describe('SubmissionSectionAccessesComponent', () => {
|
||||
let component: SubmissionSectionAccessesComponent;
|
||||
let fixture: ComponentFixture<SubmissionSectionAccessesComponent>;
|
||||
|
||||
const sectionsServiceStub = new SectionsServiceStub();
|
||||
// const pathCombiner = new JsonPatchOperationPathCombiner('sections', sectionId, 'files', fileIndex);
|
||||
|
||||
const builderService: FormBuilderService = getMockFormBuilderService();
|
||||
const submissionAccessesConfigService = getSubmissionAccessesConfigService();
|
||||
const sectionAccessesService = getSectionAccessesService();
|
||||
const sectionFormOperationsService = getMockFormOperationsService();
|
||||
const operationsBuilder = jasmine.createSpyObj('operationsBuilder', {
|
||||
add: undefined,
|
||||
remove: undefined,
|
||||
replace: undefined,
|
||||
});
|
||||
|
||||
let formService: any;
|
||||
|
||||
const storeStub = jasmine.createSpyObj('store', ['dispatch']);
|
||||
|
||||
const sectionData = {
|
||||
header: 'submit.progressbar.accessCondition',
|
||||
config: 'http://localhost:8080/server/api/config/submissionaccessoptions/AccessConditionDefaultConfiguration',
|
||||
mandatory: true,
|
||||
sectionType: 'accessCondition',
|
||||
collapsed: false,
|
||||
enabled: true,
|
||||
data: {
|
||||
discoverable: true,
|
||||
accessConditions: []
|
||||
},
|
||||
errorsToShow: [],
|
||||
serverValidationErrors: [],
|
||||
isLoading: false,
|
||||
isValid: true
|
||||
};
|
||||
|
||||
describe('First with canChangeDiscoverable true', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
TranslateModule.forRoot()
|
||||
],
|
||||
declarations: [SubmissionSectionAccessesComponent, FormComponent],
|
||||
providers: [
|
||||
{ provide: SectionsService, useValue: sectionsServiceStub },
|
||||
{ provide: FormBuilderService, useValue: builderService },
|
||||
{ provide: SubmissionAccessesConfigService, useValue: submissionAccessesConfigService },
|
||||
{ provide: SectionAccessesService, useValue: sectionAccessesService },
|
||||
{ provide: SectionFormOperationsService, useValue: sectionFormOperationsService },
|
||||
{ provide: JsonPatchOperationsBuilder, useValue: operationsBuilder },
|
||||
{ provide: TranslateService, useValue: getMockTranslateService() },
|
||||
{ provide: FormService, useValue: getMockFormService() },
|
||||
{ provide: Store, useValue: storeStub },
|
||||
{ provide: SubmissionJsonPatchOperationsService, useValue: SubmissionJsonPatchOperationsServiceStub },
|
||||
{ provide: 'sectionDataProvider', useValue: sectionData },
|
||||
{ provide: 'submissionIdProvider', useValue: '1508' },
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(inject([Store], (store: Store<AppState>) => {
|
||||
fixture = TestBed.createComponent(SubmissionSectionAccessesComponent);
|
||||
component = fixture.componentInstance;
|
||||
formService = TestBed.inject(FormService);
|
||||
formService.validateAllFormFields.and.callFake(() => null);
|
||||
formService.isValid.and.returnValue(observableOf(true));
|
||||
formService.getFormData.and.returnValue(observableOf(mockAccessesFormData));
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have created formModel', () => {
|
||||
expect(component.formModel).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have formModel length should be 2', () => {
|
||||
expect(component.formModel.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('formModel should have 1 model type checkbox and 1 model type array', () => {
|
||||
expect(component.formModel[0] instanceof DynamicCheckboxModel).toBeTrue();
|
||||
expect(component.formModel[1] instanceof DynamicFormArrayModel).toBeTrue();
|
||||
});
|
||||
|
||||
it('formModel type array should have formgroup with 1 input and 2 datepickers', () => {
|
||||
const formModel: any = component.formModel[1];
|
||||
const formGroup = formModel.groupFactory()[0].group;
|
||||
expect(formGroup[0] instanceof DynamicSelectModel).toBeTrue();
|
||||
expect(formGroup[1] instanceof DynamicDatePickerModel).toBeTrue();
|
||||
expect(formGroup[2] instanceof DynamicDatePickerModel).toBeTrue();
|
||||
});
|
||||
|
||||
it('when checkbox changed it should call operationsBuilder replace function', () => {
|
||||
component.onChange(checkboxChangeEvent);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(operationsBuilder.replace).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('when dropdown select changed it should call operationsBuilder add function', () => {
|
||||
component.onChange(accessConditionChangeEvent);
|
||||
fixture.detectChanges();
|
||||
expect(operationsBuilder.add).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when canDescoverable is false', () => {
|
||||
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
TranslateModule.forRoot()
|
||||
],
|
||||
declarations: [SubmissionSectionAccessesComponent, FormComponent],
|
||||
providers: [
|
||||
{ provide: SectionsService, useValue: sectionsServiceStub },
|
||||
{ provide: FormBuilderService, useValue: builderService },
|
||||
{ provide: SubmissionAccessesConfigService, useValue: getSubmissionAccessesConfigNotChangeDiscoverableService() },
|
||||
{ provide: SectionAccessesService, useValue: sectionAccessesService },
|
||||
{ provide: SectionFormOperationsService, useValue: sectionFormOperationsService },
|
||||
{ provide: JsonPatchOperationsBuilder, useValue: operationsBuilder },
|
||||
{ provide: TranslateService, useValue: getMockTranslateService() },
|
||||
{ provide: FormService, useValue: getMockFormService() },
|
||||
{ provide: Store, useValue: storeStub },
|
||||
{ provide: SubmissionJsonPatchOperationsService, useValue: SubmissionJsonPatchOperationsServiceStub },
|
||||
{ provide: 'sectionDataProvider', useValue: sectionData },
|
||||
{ provide: 'submissionIdProvider', useValue: '1508' },
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(inject([Store], (store: Store<AppState>) => {
|
||||
fixture = TestBed.createComponent(SubmissionSectionAccessesComponent);
|
||||
component = fixture.componentInstance;
|
||||
formService = TestBed.inject(FormService);
|
||||
formService.validateAllFormFields.and.callFake(() => null);
|
||||
formService.isValid.and.returnValue(observableOf(true));
|
||||
formService.getFormData.and.returnValue(observableOf(mockAccessesFormData));
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
|
||||
it('should have formModel length should be 1', () => {
|
||||
expect(component.formModel.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('formModel should have only 1 model type array', () => {
|
||||
expect(component.formModel[0] instanceof DynamicFormArrayModel).toBeTrue();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@@ -0,0 +1,379 @@
|
||||
import { SectionAccessesService } from './section-accesses.service';
|
||||
import { Component, Inject, ViewChild } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
|
||||
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 {
|
||||
DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX,
|
||||
DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER,
|
||||
DynamicCheckboxModel,
|
||||
DynamicDatePickerModel,
|
||||
DynamicFormArrayModel,
|
||||
DynamicFormControlEvent,
|
||||
DynamicFormControlModel,
|
||||
DynamicFormGroupModel,
|
||||
DynamicSelectModel,
|
||||
MATCH_ENABLED,
|
||||
OR_OPERATOR
|
||||
} from '@ng-dynamic-forms/core';
|
||||
|
||||
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,
|
||||
FORM_ACCESS_CONDITION_START_DATE_LAYOUT,
|
||||
FORM_ACCESS_CONDITION_TYPE_CONFIG,
|
||||
FORM_ACCESS_CONDITION_TYPE_LAYOUT
|
||||
} from './section-accesses.model';
|
||||
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 { 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 { 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',
|
||||
styleUrls: ['./section-accesses.component.scss']
|
||||
})
|
||||
@renderSectionFor(SectionsType.AccessesCondition)
|
||||
export class SubmissionSectionAccessesComponent extends SectionModelComponent {
|
||||
|
||||
/**
|
||||
* The FormComponent reference
|
||||
*/
|
||||
@ViewChild('formRef') public formRef: FormComponent;
|
||||
|
||||
/**
|
||||
* List of available access conditions that could be set to item
|
||||
*/
|
||||
public availableAccessConditionOptions: AccessesConditionOption[]; // List of accessConditions that an user can select
|
||||
|
||||
/**
|
||||
* The form id
|
||||
* @type {string}
|
||||
*/
|
||||
public formId: string;
|
||||
|
||||
/**
|
||||
* The accesses section data
|
||||
* @type {WorkspaceitemSectionAccessesObject}
|
||||
*/
|
||||
public accessesData: WorkspaceitemSectionAccessesObject;
|
||||
|
||||
/**
|
||||
* The form model
|
||||
* @type {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;
|
||||
|
||||
/**
|
||||
* Defines if the access discoverable property can be managed
|
||||
*/
|
||||
public canChangeDiscoverable: boolean;
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*
|
||||
* @param {SectionsService} sectionService
|
||||
* @param {SectionDataObject} injectedSectionData
|
||||
* @param {FormService} formService
|
||||
* @param {JsonPatchOperationsBuilder} operationsBuilder
|
||||
* @param {SectionFormOperationsService} formOperationsService
|
||||
* @param {FormBuilderService} formBuilderService
|
||||
* @param {TranslateService} translate
|
||||
* @param {SubmissionAccessesConfigService} accessesConfigService
|
||||
* @param {SectionAccessesService} accessesService
|
||||
* @param {SubmissionJsonPatchOperationsService} operationsService
|
||||
* @param {string} injectedSubmissionId
|
||||
*/
|
||||
constructor(
|
||||
protected sectionService: SectionsService,
|
||||
private formBuilderService: FormBuilderService,
|
||||
private accessesConfigService: SubmissionAccessesConfigService,
|
||||
private accessesService: SectionAccessesService,
|
||||
protected formOperationsService: SectionFormOperationsService,
|
||||
protected operationsBuilder: JsonPatchOperationsBuilder,
|
||||
private formService: FormService,
|
||||
private translate: TranslateService,
|
||||
private operationsService: SubmissionJsonPatchOperationsService,
|
||||
@Inject('sectionDataProvider') public injectedSectionData: SectionDataObject,
|
||||
@Inject('submissionIdProvider') public injectedSubmissionId: string) {
|
||||
super(undefined, injectedSectionData, injectedSubmissionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize form model values
|
||||
*
|
||||
* @param formModel
|
||||
* The form model
|
||||
*/
|
||||
public initModelData(formModel: DynamicFormControlModel[]) {
|
||||
this.accessesData.accessConditions.forEach((accessCondition, index) => {
|
||||
Array.of('name', 'startDate', 'endDate')
|
||||
.filter((key) => accessCondition.hasOwnProperty(key) && isNotEmpty(accessCondition[key]))
|
||||
.forEach((key) => {
|
||||
const metadataModel: any = this.formBuilderService.findById(key, formModel, index);
|
||||
if (metadataModel) {
|
||||
if (metadataModel.type === DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER) {
|
||||
const date = new Date(accessCondition[key]);
|
||||
metadataModel.value = {
|
||||
year: date.getUTCFullYear(),
|
||||
month: date.getUTCMonth() + 1,
|
||||
day: date.getUTCDate()
|
||||
};
|
||||
} else {
|
||||
metadataModel.value = accessCondition[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when a form dfChange event is fired.
|
||||
* Dispatch form operations based on changes.
|
||||
*/
|
||||
onChange(event: DynamicFormControlEvent) {
|
||||
if (event.model.type === DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX) {
|
||||
const path = this.formOperationsService.getFieldPathSegmentedFromChangeEvent(event);
|
||||
const value = this.formOperationsService.getFieldValueFromChangeEvent(event);
|
||||
this.operationsBuilder.replace(this.pathCombiner.getPath(path), value.value, true);
|
||||
} else {
|
||||
if (event.model.id === FORM_ACCESS_CONDITION_TYPE_CONFIG.id) {
|
||||
// Clear previous state when switching through different access conditions
|
||||
|
||||
const startDateControl: FormControl = event.control.parent.get('startDate') as FormControl;
|
||||
const endDateControl: FormControl = event.control.parent.get('endDate') as FormControl;
|
||||
|
||||
startDateControl?.markAsUntouched();
|
||||
endDateControl?.markAsUntouched();
|
||||
|
||||
startDateControl?.setValue(null);
|
||||
endDateControl?.setValue(null);
|
||||
event.control.parent.markAsDirty();
|
||||
}
|
||||
|
||||
// validate form
|
||||
this.formService.validateAllFormFields(this.formRef.formGroup);
|
||||
this.formService.isValid(this.formId).pipe(
|
||||
take(1),
|
||||
filter((isValid) => isValid),
|
||||
mergeMap(() => this.formService.getFormData(this.formId)),
|
||||
take(1)
|
||||
).subscribe((formData: any) => {
|
||||
const accessConditionsToSave = [];
|
||||
formData.accessCondition
|
||||
.map((accessConditions) => accessConditions.accessConditionGroup)
|
||||
.filter((accessCondition) => isNotEmpty(accessCondition))
|
||||
.forEach((accessCondition) => {
|
||||
let accessConditionOpt;
|
||||
|
||||
this.availableAccessConditionOptions
|
||||
.filter((element) => isNotNull(accessCondition.name) && element.name === accessCondition.name[0].value)
|
||||
.forEach((element) => accessConditionOpt = element);
|
||||
|
||||
if (accessConditionOpt) {
|
||||
const currentAccessCondition = Object.assign({}, accessCondition);
|
||||
currentAccessCondition.name = this.retrieveValueFromField(accessCondition.name);
|
||||
|
||||
/* When start and end date fields are deactivated, their values may be still present in formData,
|
||||
therefore it is necessary to delete them if they're not allowed by the current access condition option. */
|
||||
if (!accessConditionOpt.hasStartDate) {
|
||||
delete currentAccessCondition.startDate;
|
||||
} else if (accessCondition.startDate) {
|
||||
const startDate = this.retrieveValueFromField(accessCondition.startDate);
|
||||
currentAccessCondition.startDate = dateToISOFormat(startDate);
|
||||
}
|
||||
if (!accessConditionOpt.hasEndDate) {
|
||||
delete currentAccessCondition.endDate;
|
||||
} else if (accessCondition.endDate) {
|
||||
const endDate = this.retrieveValueFromField(accessCondition.endDate);
|
||||
currentAccessCondition.endDate = dateToISOFormat(endDate);
|
||||
}
|
||||
accessConditionsToSave.push(currentAccessCondition);
|
||||
}
|
||||
});
|
||||
|
||||
this.operationsBuilder.add(this.pathCombiner.getPath('accessConditions'), accessConditionsToSave, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when a form removeArrayItem event is fired.
|
||||
* Dispatch remove form operations based on changes.
|
||||
*/
|
||||
onRemove(event: DynamicFormControlEvent) {
|
||||
const fieldIndex = this.formOperationsService.getArrayIndexFromEvent(event);
|
||||
const fieldPath = 'accessConditions/' + fieldIndex;
|
||||
|
||||
this.operationsBuilder.remove(this.pathCombiner.getPath(fieldPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from all subscriptions
|
||||
*/
|
||||
onSectionDestroy() {
|
||||
this.subs
|
||||
.filter((subscription) => hasValue(subscription))
|
||||
.forEach((subscription) => subscription.unsubscribe());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all instance variables and retrieve collection default access conditions
|
||||
*/
|
||||
protected onSectionInit(): void {
|
||||
|
||||
this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id);
|
||||
this.formId = this.formService.getUniqueId(this.sectionData.id);
|
||||
const config$ = this.accessesConfigService.findByHref(this.sectionData.config, true, false).pipe(
|
||||
getFirstSucceededRemoteData(),
|
||||
map((config) => config.payload),
|
||||
);
|
||||
|
||||
const accessData$ = this.accessesService.getAccessesData(this.submissionId, this.sectionData.id);
|
||||
|
||||
combineLatest([config$, accessData$]).subscribe(([config, accessData]) => {
|
||||
this.availableAccessConditionOptions = isNotEmpty(config.accessConditionOptions) ? config.accessConditionOptions : [];
|
||||
this.canChangeDiscoverable = !!config.canChangeDiscoverable;
|
||||
this.accessesData = accessData;
|
||||
this.formModel = this.buildFileEditForm();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get section status
|
||||
*
|
||||
* @return Observable<boolean>
|
||||
* the section status
|
||||
*/
|
||||
protected getSectionStatus(): Observable<boolean> {
|
||||
return of(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize form model
|
||||
*/
|
||||
protected buildFileEditForm() {
|
||||
|
||||
const formModel: DynamicFormControlModel[] = [];
|
||||
if (this.canChangeDiscoverable) {
|
||||
const discoverableCheckboxConfig = Object.assign({}, ACCESS_FORM_CHECKBOX_CONFIG, {
|
||||
label: this.translate.instant('submission.sections.accesses.form.discoverable-label'),
|
||||
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 = [];
|
||||
|
||||
for (const accessCondition of this.availableAccessConditionOptions) {
|
||||
accessConditionTypeOptions.push(
|
||||
{
|
||||
label: accessCondition.name,
|
||||
value: accessCondition.name
|
||||
}
|
||||
);
|
||||
}
|
||||
accessConditionTypeModelConfig.options = accessConditionTypeOptions;
|
||||
|
||||
// Dynamically assign of relation in config. For startdate, endDate, groups.
|
||||
const hasStart = [];
|
||||
const hasEnd = [];
|
||||
const hasGroups = [];
|
||||
this.availableAccessConditionOptions.forEach((condition) => {
|
||||
const showStart: boolean = condition.hasStartDate === true;
|
||||
const showEnd: boolean = condition.hasEndDate === true;
|
||||
const showGroups: boolean = showStart || showEnd;
|
||||
if (showStart) {
|
||||
hasStart.push({ id: 'name', value: condition.name });
|
||||
}
|
||||
if (showEnd) {
|
||||
hasEnd.push({ id: 'name', value: condition.name });
|
||||
}
|
||||
if (showGroups) {
|
||||
hasGroups.push({ id: 'name', value: condition.name });
|
||||
}
|
||||
});
|
||||
const confStart = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasStart }] };
|
||||
const confEnd = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasEnd }] };
|
||||
|
||||
accessConditionsArrayConfig.groupFactory = () => {
|
||||
const type = new DynamicSelectModel(accessConditionTypeModelConfig, FORM_ACCESS_CONDITION_TYPE_LAYOUT);
|
||||
const startDateConfig = Object.assign({}, FORM_ACCESS_CONDITION_START_DATE_CONFIG, confStart);
|
||||
const endDateConfig = Object.assign({}, FORM_ACCESS_CONDITION_END_DATE_CONFIG, confEnd);
|
||||
|
||||
const startDate = new DynamicDatePickerModel(startDateConfig, FORM_ACCESS_CONDITION_START_DATE_LAYOUT);
|
||||
const endDate = new DynamicDatePickerModel(endDateConfig, FORM_ACCESS_CONDITION_END_DATE_LAYOUT);
|
||||
const accessConditionGroupConfig = Object.assign({}, ACCESS_CONDITION_GROUP_CONFIG);
|
||||
accessConditionGroupConfig.group = [type];
|
||||
if (hasStart.length > 0) {
|
||||
accessConditionGroupConfig.group.push(startDate);
|
||||
}
|
||||
if (hasEnd.length > 0) {
|
||||
accessConditionGroupConfig.group.push(endDate);
|
||||
}
|
||||
return [new DynamicFormGroupModel(accessConditionGroupConfig, ACCESS_CONDITION_GROUP_LAYOUT)];
|
||||
};
|
||||
|
||||
// Number of access conditions blocks in form
|
||||
accessConditionsArrayConfig.initialCount = isNotEmpty(this.accessesData.accessConditions) ? this.accessesData.accessConditions.length : 1;
|
||||
formModel.push(
|
||||
new DynamicFormArrayModel(accessConditionsArrayConfig, ACCESS_CONDITIONS_FORM_ARRAY_LAYOUT)
|
||||
);
|
||||
|
||||
this.initModelData(formModel);
|
||||
return formModel;
|
||||
}
|
||||
|
||||
protected retrieveValueFromField(field: any) {
|
||||
const temp = Array.isArray(field) ? field[0] : field;
|
||||
return (temp) ? temp.value : undefined;
|
||||
}
|
||||
|
||||
}
|
123
src/app/submission/sections/accesses/section-accesses.model.ts
Normal file
123
src/app/submission/sections/accesses/section-accesses.model.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import {
|
||||
DynamicDatePickerModelConfig,
|
||||
DynamicFormArrayModelConfig,
|
||||
DynamicFormControlLayout,
|
||||
DynamicFormGroupModelConfig,
|
||||
DynamicSelectModelConfig,
|
||||
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_CONFIG: DynamicCheckboxModelConfig = {
|
||||
id: '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',
|
||||
group: []
|
||||
};
|
||||
|
||||
export const ACCESS_CONDITION_GROUP_LAYOUT: DynamicFormControlLayout = {
|
||||
element: {
|
||||
host: 'form-group access-condition-group col',
|
||||
container: 'pl-1 pr-1',
|
||||
control: 'form-row '
|
||||
}
|
||||
};
|
||||
|
||||
export const ACCESS_CONDITIONS_FORM_ARRAY_CONFIG: DynamicFormArrayModelConfig = {
|
||||
id: 'accessCondition',
|
||||
groupFactory: null,
|
||||
};
|
||||
export const ACCESS_CONDITIONS_FORM_ARRAY_LAYOUT: DynamicFormControlLayout = {
|
||||
grid: {
|
||||
group: 'form-row pt-4',
|
||||
}
|
||||
};
|
||||
|
||||
export const FORM_ACCESS_CONDITION_TYPE_CONFIG: DynamicSelectModelConfig<any> = {
|
||||
id: 'name',
|
||||
label: 'submission.sections.accesses.form.access-condition-label',
|
||||
hint: 'submission.sections.accesses.form.access-condition-hint',
|
||||
options: []
|
||||
};
|
||||
export const FORM_ACCESS_CONDITION_TYPE_LAYOUT: DynamicFormControlLayout = {
|
||||
element: {
|
||||
host: 'col-12',
|
||||
label: 'col-form-label name-label'
|
||||
}
|
||||
};
|
||||
|
||||
export const FORM_ACCESS_CONDITION_START_DATE_CONFIG: DynamicDatePickerModelConfig = {
|
||||
id: 'startDate',
|
||||
label: 'submission.sections.accesses.form.from-label',
|
||||
hint: 'submission.sections.accesses.form.from-hint',
|
||||
placeholder: 'submission.sections.accesses.form.from-placeholder',
|
||||
inline: false,
|
||||
toggleIcon: 'far fa-calendar-alt',
|
||||
relations: [
|
||||
{
|
||||
match: MATCH_ENABLED,
|
||||
operator: OR_OPERATOR,
|
||||
when: []
|
||||
}
|
||||
],
|
||||
required: true,
|
||||
validators: {
|
||||
required: null
|
||||
},
|
||||
errorMessages: {
|
||||
required: 'submission.sections.accesses.form.date-required-from'
|
||||
}
|
||||
};
|
||||
export const FORM_ACCESS_CONDITION_START_DATE_LAYOUT: DynamicFormControlLayout = {
|
||||
element: {
|
||||
label: 'col-form-label'
|
||||
},
|
||||
grid: {
|
||||
host: 'col-6'
|
||||
}
|
||||
};
|
||||
|
||||
export const FORM_ACCESS_CONDITION_END_DATE_CONFIG: DynamicDatePickerModelConfig = {
|
||||
id: 'endDate',
|
||||
label: 'submission.sections.accesses.form.until-label',
|
||||
hint: 'submission.sections.accesses.form.until-hint',
|
||||
placeholder: 'submission.sections.accesses.form.until-placeholder',
|
||||
inline: false,
|
||||
toggleIcon: 'far fa-calendar-alt',
|
||||
relations: [
|
||||
{
|
||||
match: MATCH_ENABLED,
|
||||
operator: OR_OPERATOR,
|
||||
when: []
|
||||
}
|
||||
],
|
||||
required: true,
|
||||
validators: {
|
||||
required: null
|
||||
},
|
||||
errorMessages: {
|
||||
required: 'submission.sections.accesses.form.date-required-until'
|
||||
}
|
||||
};
|
||||
export const FORM_ACCESS_CONDITION_END_DATE_LAYOUT: DynamicFormControlLayout = {
|
||||
element: {
|
||||
label: 'col-form-label'
|
||||
},
|
||||
grid: {
|
||||
host: 'col-6'
|
||||
}
|
||||
};
|
@@ -0,0 +1,42 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, filter } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { SubmissionState } from '../../submission.reducers';
|
||||
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 item's accesses condition state.
|
||||
*/
|
||||
@Injectable()
|
||||
export class SectionAccessesService {
|
||||
|
||||
/**
|
||||
* Initialize service variables
|
||||
*
|
||||
* @param {Store<SubmissionState>} store
|
||||
*/
|
||||
constructor(private store: Store<SubmissionState>) { }
|
||||
|
||||
|
||||
/**
|
||||
* Return item's accesses condition state.
|
||||
*
|
||||
* @param submissionId
|
||||
* The submission id
|
||||
* @param sectionId
|
||||
* The section id
|
||||
* @returns {Observable}
|
||||
* Emits bitstream's metadata
|
||||
*/
|
||||
public getAccessesData(submissionId: string, sectionId: string): Observable<WorkspaceitemSectionAccessesObject> {
|
||||
|
||||
return this.store.select(submissionSectionDataFromIdSelector(submissionId, sectionId)).pipe(
|
||||
filter((state) => isNotUndefined(state)),
|
||||
distinctUntilChanged());
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@ import { SubmissionServiceStub } from '../../../shared/testing/submission-servic
|
||||
import { getMockTranslateService } from '../../../shared/mocks/translate.service.mock';
|
||||
import { SectionsService } from '../sections.service';
|
||||
import { SectionsServiceStub } from '../../../shared/testing/sections-service.stub';
|
||||
import { SubmissionSectionformComponent } from './section-form.component';
|
||||
import { SubmissionSectionFormComponent } from './section-form.component';
|
||||
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
|
||||
import { getMockFormBuilderService } from '../../../shared/mocks/form-builder-service.mock';
|
||||
import { getMockFormOperationsService } from '../../../shared/mocks/form-operations-service.mock';
|
||||
@@ -137,11 +137,11 @@ const dynamicFormControlEvent: DynamicFormControlEvent = {
|
||||
type: DynamicFormControlEventType.Change
|
||||
};
|
||||
|
||||
describe('SubmissionSectionformComponent test suite', () => {
|
||||
describe('SubmissionSectionFormComponent test suite', () => {
|
||||
|
||||
let comp: SubmissionSectionformComponent;
|
||||
let comp: SubmissionSectionFormComponent;
|
||||
let compAsAny: any;
|
||||
let fixture: ComponentFixture<SubmissionSectionformComponent>;
|
||||
let fixture: ComponentFixture<SubmissionSectionFormComponent>;
|
||||
let submissionServiceStub: SubmissionServiceStub;
|
||||
let notificationsServiceStub: NotificationsServiceStub;
|
||||
let formService: any = getMockFormService();
|
||||
@@ -167,7 +167,7 @@ describe('SubmissionSectionformComponent test suite', () => {
|
||||
],
|
||||
declarations: [
|
||||
FormComponent,
|
||||
SubmissionSectionformComponent,
|
||||
SubmissionSectionFormComponent,
|
||||
TestComponent
|
||||
],
|
||||
providers: [
|
||||
@@ -186,7 +186,7 @@ describe('SubmissionSectionformComponent test suite', () => {
|
||||
{ provide: 'submissionIdProvider', useValue: submissionId },
|
||||
{ provide: SubmissionObjectDataService, useValue: { getHrefByID: () => observableOf('testUrl'), findById: () => createSuccessfulRemoteDataObject$(new WorkspaceItem()) } },
|
||||
ChangeDetectorRef,
|
||||
SubmissionSectionformComponent
|
||||
SubmissionSectionFormComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents().then();
|
||||
@@ -215,7 +215,7 @@ describe('SubmissionSectionformComponent test suite', () => {
|
||||
testFixture.destroy();
|
||||
});
|
||||
|
||||
it('should create SubmissionSectionformComponent', inject([SubmissionSectionformComponent], (app: SubmissionSectionformComponent) => {
|
||||
it('should create SubmissionSectionFormComponent', inject([SubmissionSectionFormComponent], (app: SubmissionSectionFormComponent) => {
|
||||
|
||||
expect(app).toBeDefined();
|
||||
|
||||
@@ -224,7 +224,7 @@ describe('SubmissionSectionformComponent test suite', () => {
|
||||
|
||||
describe('', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SubmissionSectionformComponent);
|
||||
fixture = TestBed.createComponent(SubmissionSectionFormComponent);
|
||||
comp = fixture.componentInstance;
|
||||
compAsAny = comp;
|
||||
submissionServiceStub = TestBed.inject(SubmissionService as any);
|
||||
|
@@ -44,7 +44,7 @@ import { RemoteData } from '../../../core/data/remote-data';
|
||||
templateUrl: './section-form.component.html',
|
||||
})
|
||||
@renderSectionFor(SectionsType.SubmissionForm)
|
||||
export class SubmissionSectionformComponent extends SectionModelComponent {
|
||||
export class SubmissionSectionFormComponent extends SectionModelComponent {
|
||||
|
||||
/**
|
||||
* The form id
|
||||
|
@@ -4,5 +4,6 @@ export enum SectionsType {
|
||||
Upload = 'upload',
|
||||
License = 'license',
|
||||
CcLicense = 'cclicense',
|
||||
collection = 'collection'
|
||||
collection = 'collection',
|
||||
AccessesCondition = 'accessCondition',
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ export const BITSTREAM_ACCESS_CONDITION_GROUP_CONFIG: DynamicFormGroupModelConfi
|
||||
|
||||
export const BITSTREAM_ACCESS_CONDITION_GROUP_LAYOUT: DynamicFormControlLayout = {
|
||||
element: {
|
||||
host: 'form-group flex-fill access-condition-group',
|
||||
host: 'form-group access-condition-group col',
|
||||
container: 'pl-1 pr-1',
|
||||
control: 'form-row '
|
||||
}
|
||||
@@ -47,6 +47,7 @@ export const BITSTREAM_ACCESS_CONDITIONS_FORM_ARRAY_LAYOUT: DynamicFormControlLa
|
||||
export const BITSTREAM_FORM_ACCESS_CONDITION_TYPE_CONFIG: DynamicSelectModelConfig<any> = {
|
||||
id: 'name',
|
||||
label: 'submission.sections.upload.form.access-condition-label',
|
||||
hint: 'submission.sections.upload.form.access-condition-hint',
|
||||
options: []
|
||||
};
|
||||
export const BITSTREAM_FORM_ACCESS_CONDITION_TYPE_LAYOUT: DynamicFormControlLayout = {
|
||||
@@ -59,6 +60,7 @@ export const BITSTREAM_FORM_ACCESS_CONDITION_TYPE_LAYOUT: DynamicFormControlLayo
|
||||
export const BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_CONFIG: DynamicDatePickerModelConfig = {
|
||||
id: 'startDate',
|
||||
label: 'submission.sections.upload.form.from-label',
|
||||
hint: 'submission.sections.upload.form.from-hint',
|
||||
placeholder: 'submission.sections.upload.form.from-placeholder',
|
||||
inline: false,
|
||||
toggleIcon: 'far fa-calendar-alt',
|
||||
@@ -89,6 +91,7 @@ export const BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_LAYOUT: DynamicFormContr
|
||||
export const BITSTREAM_FORM_ACCESS_CONDITION_END_DATE_CONFIG: DynamicDatePickerModelConfig = {
|
||||
id: 'endDate',
|
||||
label: 'submission.sections.upload.form.until-label',
|
||||
hint: 'submission.sections.upload.form.until-hint',
|
||||
placeholder: 'submission.sections.upload.form.until-placeholder',
|
||||
inline: false,
|
||||
toggleIcon: 'far fa-calendar-alt',
|
||||
|
@@ -69,3 +69,4 @@ export function submissionSectionServerErrorsFromIdSelector(submissionId: string
|
||||
const submissionIdSelector = submissionSectionFromIdSelector(submissionId, sectionId);
|
||||
return subStateSelector<SubmissionState, SubmissionSectionObject>(submissionIdSelector, 'serverValidationErrors');
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
||||
import { CoreModule } from '../core/core.module';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
import { SubmissionSectionformComponent } from './sections/form/section-form.component';
|
||||
import { SubmissionSectionFormComponent } from './sections/form/section-form.component';
|
||||
import { SectionsDirective } from './sections/sections.directive';
|
||||
import { SectionsService } from './sections/sections.service';
|
||||
import { SubmissionFormCollectionComponent } from './form/collection/submission-form-collection.component';
|
||||
@@ -38,14 +38,23 @@ import { ThemedSubmissionEditComponent } from './edit/themed-submission-edit.com
|
||||
import { ThemedSubmissionSubmitComponent } from './submit/themed-submission-submit.component';
|
||||
import { ThemedSubmissionImportExternalComponent } from './import-external/themed-submission-import-external.component';
|
||||
import { FormModule } from '../shared/form/form.module';
|
||||
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { NgbAccordionModule, NgbModalModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SubmissionSectionAccessesComponent } from './sections/accesses/section-accesses.component';
|
||||
import { SubmissionAccessesConfigService } from '../core/config/submission-accesses-config.service';
|
||||
import { SectionAccessesService } from './sections/accesses/section-accesses.service';
|
||||
|
||||
const DECLARATIONS = [
|
||||
SubmissionSectionUploadAccessConditionsComponent,
|
||||
const ENTRY_COMPONENTS = [
|
||||
// put only entry components that use custom decorator
|
||||
SubmissionSectionUploadComponent,
|
||||
SubmissionSectionformComponent,
|
||||
SubmissionSectionFormComponent,
|
||||
SubmissionSectionLicenseComponent,
|
||||
SubmissionSectionCcLicensesComponent,
|
||||
SubmissionSectionAccessesComponent,
|
||||
SubmissionSectionUploadFileEditComponent
|
||||
];
|
||||
|
||||
const DECLARATIONS = [
|
||||
...ENTRY_COMPONENTS,
|
||||
SectionsDirective,
|
||||
SubmissionEditComponent,
|
||||
ThemedSubmissionEditComponent,
|
||||
@@ -57,6 +66,7 @@ const DECLARATIONS = [
|
||||
ThemedSubmissionSubmitComponent,
|
||||
SubmissionUploadFilesComponent,
|
||||
SubmissionSectionContainerComponent,
|
||||
SubmissionSectionUploadAccessConditionsComponent,
|
||||
SubmissionSectionUploadFileComponent,
|
||||
SubmissionSectionUploadFileEditComponent,
|
||||
SubmissionSectionUploadFileViewComponent,
|
||||
@@ -64,14 +74,7 @@ const DECLARATIONS = [
|
||||
ThemedSubmissionImportExternalComponent,
|
||||
SubmissionImportExternalSearchbarComponent,
|
||||
SubmissionImportExternalPreviewComponent,
|
||||
SubmissionImportExternalCollectionComponent
|
||||
];
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
SubmissionSectionUploadComponent,
|
||||
SubmissionSectionformComponent,
|
||||
SubmissionSectionLicenseComponent,
|
||||
SubmissionSectionCcLicensesComponent
|
||||
SubmissionImportExternalCollectionComponent,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
@@ -84,14 +87,17 @@ const ENTRY_COMPONENTS = [
|
||||
JournalEntitiesModule.withEntryComponents(),
|
||||
ResearchEntitiesModule.withEntryComponents(),
|
||||
FormModule,
|
||||
NgbAccordionModule
|
||||
NgbAccordionModule,
|
||||
NgbModalModule
|
||||
],
|
||||
declarations: DECLARATIONS,
|
||||
exports: DECLARATIONS,
|
||||
providers: [
|
||||
SectionUploadService,
|
||||
SectionsService,
|
||||
SubmissionUploadsConfigService
|
||||
SubmissionUploadsConfigService,
|
||||
SubmissionAccessesConfigService,
|
||||
SectionAccessesService
|
||||
]
|
||||
})
|
||||
|
||||
|
@@ -3836,6 +3836,8 @@
|
||||
|
||||
|
||||
|
||||
"submission.sections.submit.progressbar.accessCondition": "Item access conditions",
|
||||
|
||||
"submission.sections.submit.progressbar.CClicense": "Creative commons license",
|
||||
|
||||
"submission.sections.submit.progressbar.describe.recycle": "Recycle",
|
||||
@@ -3892,6 +3894,8 @@
|
||||
|
||||
"submission.sections.upload.form.access-condition-label": "Access condition type",
|
||||
|
||||
"submission.sections.upload.form.access-condition-hint": "Select an access condition to apply on the bitstream once the item is deposited",
|
||||
|
||||
"submission.sections.upload.form.date-required": "Date is required.",
|
||||
|
||||
"submission.sections.upload.form.date-required-from": "Grant access from date is required.",
|
||||
@@ -3900,6 +3904,8 @@
|
||||
|
||||
"submission.sections.upload.form.from-label": "Grant access from",
|
||||
|
||||
"submission.sections.upload.form.from-hint": "Select the date from which the related access condition is applied",
|
||||
|
||||
"submission.sections.upload.form.from-placeholder": "From",
|
||||
|
||||
"submission.sections.upload.form.group-label": "Group",
|
||||
@@ -3908,6 +3914,8 @@
|
||||
|
||||
"submission.sections.upload.form.until-label": "Grant access until",
|
||||
|
||||
"submission.sections.upload.form.until-hint": "Select the date until which the related access condition is applied",
|
||||
|
||||
"submission.sections.upload.form.until-placeholder": "Until",
|
||||
|
||||
"submission.sections.upload.header.policy.default.nolist": "Uploaded files in the {{collectionName}} collection will be accessible according to the following group(s):",
|
||||
@@ -3928,6 +3936,35 @@
|
||||
|
||||
"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",
|
||||
|
||||
"submission.sections.accesses.form.access-condition-label": "Access condition type",
|
||||
|
||||
"submission.sections.accesses.form.access-condition-hint": "Select an access condition to apply on the item once it is deposited",
|
||||
|
||||
"submission.sections.accesses.form.date-required": "Date is required.",
|
||||
|
||||
"submission.sections.accesses.form.date-required-from": "Grant access from date is required.",
|
||||
|
||||
"submission.sections.accesses.form.date-required-until": "Grant access until date is required.",
|
||||
|
||||
"submission.sections.accesses.form.from-label": "Grant access from",
|
||||
|
||||
"submission.sections.accesses.form.from-hint": "Select the date from which the related access condition is applied",
|
||||
|
||||
"submission.sections.accesses.form.from-placeholder": "From",
|
||||
|
||||
"submission.sections.accesses.form.group-label": "Group",
|
||||
|
||||
"submission.sections.accesses.form.group-required": "Group is required.",
|
||||
|
||||
"submission.sections.accesses.form.until-label": "Grant access until",
|
||||
|
||||
"submission.sections.accesses.form.until-hint": "Select the date until which the related access condition is applied",
|
||||
|
||||
"submission.sections.accesses.form.until-placeholder": "Until",
|
||||
|
||||
|
||||
"submission.submit.breadcrumbs": "New submission",
|
||||
|
Reference in New Issue
Block a user