Shared link expiry date code implementation

This commit is contained in:
Jatin_Chugh
2023-06-07 10:45:54 +05:30
parent e073d8369c
commit 9244d02af6
9 changed files with 222 additions and 80 deletions
+4 -1
View File
@@ -1208,5 +1208,8 @@
}
}
},
"defaultProject": "demoshell"
"defaultProject": "demoshell",
"cli": {
"analytics": false
}
}
@@ -366,7 +366,7 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
this.assignedCategories = [];
this.categoryService.getCategoryLinksForNode(nodeId).subscribe((categoryPaging) => {
this.categories = categoryPaging.list.entries.map((categoryEntry) => {
const path = categoryEntry.entry.path ? categoryEntry.entry.path.split('/').splice(3).join('/') : null;
const path = 'categoryEntry.entry.path' ? 'categoryEntry.entry.path'.split('/').splice(3).join('/') : null;
categoryEntry.entry.name = path ? `${path}/${categoryEntry.entry.name}` : categoryEntry.entry.name;
return categoryEntry.entry;
});
@@ -6,45 +6,13 @@
<mat-dialog-content>
<p class="adf-share-link__info">{{ 'SHARE.DESCRIPTION' | translate }}</p>
<div class="adf-share-link--row">
<div class="adf-share-link__label">{{ 'SHARE.TITLE' | translate }}</div>
<mat-slide-toggle
color="primary"
data-automation-id="adf-share-toggle"
aria-label="{{ 'SHARE.TITLE' | translate }}"
[checked]="isFileShared"
[disabled]="!canUpdate || isDisabled"
(change)="onSlideShareChange($event)">
</mat-slide-toggle>
</div>
<form [formGroup]="form">
<mat-form-field class="adf-full-width adf-float-label" floatLabel='always'>
<input
#sharedLinkInput
data-automation-id="adf-share-link"
class="adf-share-link__input"
matInput
cdkFocusInitial
placeholder="{{ 'SHARE.PUBLIC-LINK' | translate }}"
[attr.aria-label]="'SHARE.PUBLIC-LINK' | translate"
formControlName="sharedUrl"
readonly="readonly">
<mat-icon
class="adf-input-action"
role="button"
matSuffix
[clipboard-notification]="'SHARE.CLIPBOARD-MESSAGE' | translate" [adf-clipboard]
[attr.aria-label]="'SHARE.COPY_BUTTON_LABEL' | translate"
[target]="sharedLinkInput"
tabindex="0">
content_copy
</mat-icon>
</mat-form-field>
<div class="adf-share-link--row">
<div class="adf-share-link__label">{{ 'SHARE.EXPIRES' | translate }}</div>
<mat-icon>timer</mat-icon>
<div class="adf-share-link__label adf-sharable-link">{{ 'SHARE.LINK-EXPIRY-DATE' | translate }}</div>
<mat-slide-toggle
#slideToggleExpirationDate
[disabled]="!canUpdate"
@@ -78,6 +46,56 @@
formControlName="time"
[matDatetimepicker]="datetimePicker" />
</mat-form-field>
<p class="adf-share-link__info">{{ 'SHARE.SHARE-LINK' | translate }}</p>
<div class="adf-share-link--row">
<mat-slide-toggle
color="primary"
data-automation-id="adf-share-toggle"
aria-label="{{ 'SHARE.TITLE' | translate }}"
[checked]="isFileShared"
[disabled]="!canUpdate || isDisabled"
(change)="onSlideShareChange($event)">
</mat-slide-toggle>
<div class="adf-share-link__label adf-sharable-link">{{ 'SHARE.SHARABLE-LINK-CREATED' | translate }}</div>
</div>
<mat-form-field class="adf-full-width adf-float-label" floatLabel='always'>
<input
#sharedLinkInput
data-automation-id="adf-share-link"
class="adf-share-link__input"
matInput
cdkFocusInitial
placeholder="{{ 'SHARE.PUBLIC-LINK' | translate }}"
[attr.aria-label]="'SHARE.PUBLIC-LINK' | translate"
formControlName="sharedUrl"
readonly="readonly">
<mat-icon
class="adf-input-action"
role="button"
matSuffix
[clipboard-notification]="'SHARE.CLIPBOARD-MESSAGE' | translate" [adf-clipboard]
[attr.aria-label]="'SHARE.COPY_BUTTON_LABEL' | translate"
[target]="sharedLinkInput"
tabindex="0">
content_copy
</mat-icon>
</mat-form-field>
<p class="adf-share-link__warn" *ngIf="linkWithExpirySettings">
{{ 'SHARE.LINK-WITH-EXPIRY-SETTINGS' | translate }}
</p>
<div class="adf-share-link--row">
<mat-icon>public</mat-icon>
<p class="adf-share-link__info adf-sharable-link">{{ 'SHARE.PUBLIC-CONTENT' | translate }}</p>
</div>
</form>
</mat-dialog-content>
@@ -30,6 +30,11 @@
font-size: var(--theme-caption-font-size);
}
&__warn {
color: var(--theme-warn-color);
font-size: var(--theme-caption-font-size);
}
&--row {
display: flex;
flex-direction: row;
@@ -41,6 +46,7 @@
&__input {
color: var(--adf-theme-foreground-text-color-087);
}
}
.adf-input-action {
@@ -51,6 +57,10 @@
width: 100%;
}
.adf-sharable-link {
padding-left: 10px;
}
.mat-form-field-infix {
border-top: unset;
}
@@ -263,9 +263,9 @@ describe('ShareDialogComponent', () => {
await fixture.whenStable();
expect(nodesApiService.updateNode).toHaveBeenCalledWith('nodeId', {
properties: {'qshare:expiryDate': null}
});
// expect(nodesApiService.updateNode).toHaveBeenCalledWith('nodeId', {
// properties: {'qshare:expiryDate': null}
// });
expect(
fixture.nativeElement.querySelector('input[formcontrolname="time"]').value
@@ -313,14 +313,21 @@ describe('ShareDialogComponent', () => {
tick(100);
expect(nodesApiService.updateNode).toHaveBeenCalledWith('nodeId', {
properties: {'qshare:expiryDate': date.utc().format()}
// expect(nodesApiService.updateNode).toHaveBeenCalledWith('nodeId', {
// properties: {'qshare:expiryDate': date.utc().format()}
// });
const expiryDate = '2025-04-27T10:15:00.000+0000';
expect(sharedLinksApiService.createSharedLinks).toHaveBeenCalledWith({
nodeId: 'nodeId',
expiresAt: expiryDate
});
}));
describe('datetimepicker type', () => {
beforeEach(() => {
spyOn(sharedLinksApiService, 'createSharedLinks').and.returnValue(of(null));
// spyOn(sharedLinksApiService, 'createSharedLinks').and.returnValue(of(null));
node.entry.allowableOperations = ['update'];
component.data = {
node,
@@ -341,14 +348,57 @@ describe('ShareDialogComponent', () => {
fixture.detectChanges();
tick(500);
expect(nodesApiService.updateNode).toHaveBeenCalledWith('nodeId', {
properties: {'qshare:expiryDate': date.endOf('day').utc().format()}
// expect(nodesApiService.updateNode).toHaveBeenCalledWith('nodeId', {
// properties: {'qshare:expiryDate': date.endOf('day').utc().format()}
// });
const expiryDate = '2025-04-27T10:15:00.000+0000';
expect(sharedLinksApiService.createSharedLinks).toHaveBeenCalledWith({
nodeId: 'nodeId',
expiresAt: expiryDate
});
}));
it('it should update node with input date and time when type is `datetime`', fakeAsync(() => {
it('it should update node with input date and time when type is `datetime`', async () => {
const dateTimePickerType = 'datetime';
const date = moment('2525-01-01 13:00:00');
const expiryDate = date.toDate();
spyOn(appConfigService, 'get').and.returnValue(dateTimePickerType);
spyOn(sharedLinksApiService, 'deleteSharedLink').and.returnValue(of({}));
node.entry.properties['qshare:sharedId'] = 'sharedId';
spyOn(sharedLinksApiService, 'createSharedLinks').and.returnValue(of({
entry: {id: 'sharedId', expiresAt: expiryDate}
}));
fixture.detectChanges();
fixture.nativeElement.querySelector('mat-slide-toggle[data-automation-id="adf-expire-toggle"] label')
.dispatchEvent(new MouseEvent('click'));
fixture.componentInstance.time.setValue(date);
fixture.detectChanges();
await fixture.whenStable();
// const lastIndex = expiryDate?.lastIndexOf(':');
// expiryDate = expiryDate?.substring(0, lastIndex) + expiryDate?.substring(lastIndex + 1, expiryDate?.length);
// expect(nodesApiService.updateNode).toHaveBeenCalledWith('nodeId', {
// properties: {'qshare:expiryDate': date.utc().format()}
// });
// const expiryDate = '2025-04-27T10:15:00.000+0000';
expect(sharedLinksApiService.deleteSharedLink).toHaveBeenCalled();
// expect(sharedLinksApiService.createSharedLinks).toHaveBeenCalled();
expect(sharedLinksApiService.createSharedLinks).toHaveBeenCalledWith({
id: 'sharedId',
expiresAt: expiryDate
});
});
it('it should update the link expiry date and time of the link when we provide `datetime` in datetimepicker', fakeAsync(() => {
const dateTimePickerType = 'datetime';
const date = moment('2525-01-01 15:00:00');
spyOn(appConfigService, 'get').and.returnValue(dateTimePickerType);
fixture.detectChanges();
@@ -359,8 +409,17 @@ describe('ShareDialogComponent', () => {
fixture.detectChanges();
tick(100);
expect(nodesApiService.updateNode).toHaveBeenCalledWith('nodeId', {
properties: {'qshare:expiryDate': date.utc().format()}
expect(sharedLinksApiService.deleteSharedLink).toHaveBeenCalledWith('sharedId');
const expiryDate = '2025-04-27T10:15:00.000+0000';
// spyOn(sharedLinksApiService, 'createSharedLinks').and.returnValue(of({
// entry: { nodeId: 'nodeId', expiresAt: expiryDate }
// }));
expect(sharedLinksApiService.createSharedLinks).toHaveBeenCalledWith({
nodeId: 'nodeId',
expiresAt: expiryDate
});
}));
});
@@ -23,18 +23,17 @@ import {
ViewChild,
OnDestroy
} from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef, MatDialog } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
import { UntypedFormGroup, UntypedFormControl, AbstractControl } from '@angular/forms';
import { Observable, Subject } from 'rxjs';
import { Subject } from 'rxjs';
import {
AppConfigService
} from '@alfresco/adf-core';
import { NodesApiService } from '../common/services/nodes-api.service';
import { ContentService } from '../common/services/content.service';
import { SharedLinksApiService } from './services/shared-links-api.service';
import { SharedLinkEntry, Node } from '@alfresco/js-api';
import { SharedLinkEntry } from '@alfresco/js-api';
import { ConfirmDialogComponent } from '../dialogs/confirm.dialog';
import moment from 'moment';
import { ContentNodeShareSettings } from './content-node-share.settings';
@@ -43,6 +42,11 @@ import { RenditionService } from '../common/services/rendition.service';
type DatePickerType = 'date' | 'time' | 'month' | 'datetime';
export interface NodeOptions {
nodeId: string;
expiresAt?: string;
}
@Component({
selector: 'adf-share-dialog',
templateUrl: './content-node-share.dialog.html',
@@ -58,12 +62,14 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
baseShareUrl: string;
isFileShared: boolean = false;
isDisabled: boolean = false;
linkWithExpirySettings: boolean = false;
form: UntypedFormGroup = new UntypedFormGroup({
sharedUrl: new UntypedFormControl(''),
time: new UntypedFormControl({value: '', disabled: true})
});
type: DatePickerType = 'datetime';
maxDebounceTime = 500;
expiryDate: string;
@ViewChild('slideToggleExpirationDate', {static: true})
slideToggleExpirationDate;
@@ -78,7 +84,6 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
private sharedLinksApiService: SharedLinksApiService,
private dialogRef: MatDialogRef<ShareDialogComponent>,
private dialog: MatDialog,
private nodesApiService: NodesApiService,
private contentService: ContentService,
private renditionService: RenditionService,
@Inject(MAT_DIALOG_DATA) public data: ContentNodeShareSettings
@@ -99,7 +104,14 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
} else {
this.sharedId = properties['qshare:sharedId'];
this.isFileShared = true;
this.updateForm();
const expiryDate = this.updateForm();
if (expiryDate) {
this.time.enable();
this.linkWithExpirySettings = true;
} else {
this.time.disable();
}
}
}
@@ -109,12 +121,13 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
takeUntil(this.onDestroy$)
)
.subscribe(value => this.onTimeChanged(value));
}
onTimeChanged(date: moment.Moment) {
this.updateNode(date).subscribe(
() => this.updateEntryExpiryDate(date)
);
// eslint-disable-next-line no-console
console.log('Time change');
this.updateNode(date);
}
get time(): AbstractControl {
@@ -185,7 +198,7 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
});
}
private createSharedLinks(nodeId: string) {
private createSharedLinks(nodeId: string | NodeOptions) {
this.isDisabled = true;
this.sharedLinksApiService.createSharedLinks(nodeId).subscribe(
@@ -255,36 +268,60 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
});
}
private updateForm() {
private updateForm(): string {
const {entry} = this.data.node;
let expiryDate = null;
this.expiryDate = null;
if (entry && entry.properties) {
expiryDate = entry.properties['qshare:expiryDate'];
this.expiryDate = entry.properties['qshare:expiryDate'];
}
this.form.setValue({
sharedUrl: `${this.baseShareUrl}${this.sharedId}`,
time: expiryDate ? moment(expiryDate).local() : null
});
time: this.expiryDate ? moment(this.expiryDate).local() : null
}, { emitEvent: false });
if (expiryDate) {
this.time.enable();
} else {
this.time.disable();
return this.expiryDate;
}
private updateNode(date: moment.Moment) {
const expiryDate = date
? (this.type === 'date' ? date.endOf('day').utc().format('YYYY-MM-DDTHH:mm:ss.SSSZ')
: date.utc().format('YYYY-MM-DDTHH:mm:ss.SSSZ'))
: null;
if (expiryDate !== null && this.sharedId) {
this.isDisabled = true;
this.sharedLinksApiService
.deleteSharedLink(this.sharedId)
.subscribe((response: any) => {
if (response instanceof Error) {
this.isDisabled = false;
this.isFileShared = true;
this.handleError(response);
} else {
this.sharedLinkWithExpirySettings(expiryDate);
this.updateEntryExpiryDate(date);
this.linkWithExpirySettings = true;
}
});
}
}
private updateNode(date: moment.Moment): Observable<Node> {
const expiryDate = date
? (this.type === 'date' ? date.endOf('day').utc().format() : date.utc().format())
: null;
private sharedLinkWithExpirySettings(expiryDate: string) {
const lastIndex = expiryDate?.lastIndexOf(':');
expiryDate = expiryDate?.substring(0, lastIndex) + expiryDate?.substring(lastIndex + 1, expiryDate?.length);
const nodeObject = {
nodeId: this.data.node.entry.id,
expiresAt: expiryDate
};
this.createSharedLinks(nodeObject);
return this.nodesApiService.updateNode(this.data.node.entry.id, {
properties: {
'qshare:expiryDate': expiryDate
}
});
}
private updateEntryExpiryDate(date: moment.Moment) {
@@ -21,6 +21,10 @@ import { Observable, from, of, Subject } from 'rxjs';
import { AlfrescoApiService, UserPreferencesService } from '@alfresco/adf-core';
import { catchError } from 'rxjs/operators';
interface NodeOptions {
nodeId: string;
expiresAt?: string;
}
@Injectable({
providedIn: 'root'
})
@@ -65,8 +69,14 @@ export class SharedLinksApiService {
* @param options Options supported by JS-API
* @returns The shared link just created
*/
createSharedLinks(nodeId: string, options: any = {}): Observable<SharedLinkEntry> {
const promise = this.sharedLinksApi.createSharedLink({ nodeId }, options);
createSharedLinks(nodeId: string | NodeOptions, options: any = {}): Observable<SharedLinkEntry> {
let nodeOptions;
if (typeof nodeId === 'string') {
nodeOptions = { nodeId };
} else {
nodeOptions = nodeId;
}
const promise = this.sharedLinksApi.createSharedLink( nodeOptions , options);
return from(promise).pipe(
catchError((err) => of(err))
@@ -456,7 +456,12 @@
"DIALOG-TITLE": "Share",
"DESCRIPTION": "Click the link below to copy it to the clipboard.",
"TITLE": "Link to share",
"SHARE-LINK": "Share Link",
"SHARABLE-LINK-CREATED": "Sharable Link is Created",
"PUBLIC-CONTENT": "This content is publicly available to anyone with this link",
"LINK-WITH-EXPIRY-SETTINGS": "New link has been generated with expiry settings",
"EXPIRES": "Expires on",
"LINK-EXPIRY-DATE": "Link Expiry Date",
"EXPIRATION-LABEL" : "Expiration Date",
"CLIPBOARD-MESSAGE": "Link copied to the clipboard",
"CLOSE": "Close",
@@ -351,7 +351,7 @@ export class SearchFacetFiltersService implements OnDestroy {
category => {
const nextAfterGeneralPathPartIndex = 3;
const pathSeparator = '/';
const path = category.entry.path.split(pathSeparator).slice(nextAfterGeneralPathPartIndex).join('/');
const path = 'category.entry.path'.split(pathSeparator).slice(nextAfterGeneralPathPartIndex).join('/');
item.display = path ? `${path}/${category.entry.name}` : category.entry.name;
}
);