[ACS-5991] ESLint fixes and code quality improvements (#8893)

* prefer-optional-chain: core

* prefer-optional-chain: content, fix typings

* prefer-optional-chain: process, fix typings

* prefer-optional-chain: process-cloud, fix typings, fix ts configs and eslint

* [ci: force] sonar errors fixes, insights lib

* [ci:force] fix security issues

* [ci:force] fix metadata e2e bug, js assignment bugs

* [ci:force] fix lint issue

* [ci:force] fix tests
This commit is contained in:
Denys Vuika
2023-09-18 09:42:16 +01:00
committed by GitHub
parent 99f591ed67
commit a1dd270c5d
203 changed files with 4155 additions and 4960 deletions

View File

@@ -18,33 +18,24 @@
import { Injectable } from '@angular/core';
import { Observable, from, throwError } from 'rxjs';
import { AlfrescoApiService, LogService } from '@alfresco/adf-core';
import {
AuditApi,
AuditAppPaging,
AuditAppEntry,
AuditApp,
AuditBodyUpdate,
AuditEntryPaging,
AuditEntryEntry
} from '@alfresco/js-api';
import { AuditApi, AuditAppPaging, AuditAppEntry, AuditApp, AuditBodyUpdate, AuditEntryPaging, AuditEntryEntry } from '@alfresco/js-api';
import { catchError } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuditService {
private _auditApi: AuditApi;
get auditApi(): AuditApi {
this._auditApi = this._auditApi ?? new AuditApi(this.apiService.getInstance());
return this._auditApi;
}
constructor(private apiService: AlfrescoApiService, private logService: LogService) {
}
constructor(private apiService: AlfrescoApiService, private logService: LogService) {}
/**
* Gets a list of audit applications.
*
* @param opts Options.
* @returns a list of the audit applications.
*/
@@ -53,14 +44,12 @@ export class AuditService {
skipCount: 0
};
const queryOptions = Object.assign({}, defaultOptions, opts);
return from(this.auditApi.listAuditApps(queryOptions))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.auditApi.listAuditApps(queryOptions)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
* Get audit application info.
*
* @param auditApplicationId The identifier of an audit application.
* @param opts Options.
* @returns status of an audit application.
@@ -70,14 +59,12 @@ export class AuditService {
auditApplicationId
};
const queryOptions = Object.assign({}, defaultOptions, opts);
return from(this.auditApi.getAuditApp(queryOptions))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.auditApi.getAuditApp(queryOptions)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
* Update audit application info.
*
* @param auditApplicationId The identifier of an audit application.
* @param auditAppBodyUpdate The audit application to update.
* @param opts Options.
@@ -86,14 +73,14 @@ export class AuditService {
updateAuditApp(auditApplicationId: string, auditAppBodyUpdate: boolean, opts?: any): Observable<AuditApp | any> {
const defaultOptions = {};
const queryOptions = Object.assign({}, defaultOptions, opts);
return from(this.auditApi.updateAuditApp(auditApplicationId, new AuditBodyUpdate({ isEnabled: auditAppBodyUpdate }), queryOptions))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.auditApi.updateAuditApp(auditApplicationId, new AuditBodyUpdate({ isEnabled: auditAppBodyUpdate }), queryOptions)).pipe(
catchError((err: any) => this.handleError(err))
);
}
/**
* List audit entries for an audit application.
*
* @param auditApplicationId The identifier of an audit application.
* @param opts Options.
* @returns a list of audit entries.
@@ -104,14 +91,14 @@ export class AuditService {
maxItems: 100
};
const queryOptions = Object.assign({}, defaultOptions, opts);
return from(this.auditApi.listAuditEntriesForAuditApp(auditApplicationId, queryOptions))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.auditApi.listAuditEntriesForAuditApp(auditApplicationId, queryOptions)).pipe(
catchError((err: any) => this.handleError(err))
);
}
/**
* Get audit entry.
*
* @param auditApplicationId The identifier of an audit application.
* @param auditEntryId The identifier of an audit entry.
* @param opts Options.
@@ -120,14 +107,14 @@ export class AuditService {
getAuditEntry(auditApplicationId: string, auditEntryId: string, opts?: any): Observable<AuditEntryEntry> {
const defaultOptions = {};
const queryOptions = Object.assign({}, defaultOptions, opts);
return from(this.auditApi.getAuditEntry(auditApplicationId, auditEntryId, queryOptions))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.auditApi.getAuditEntry(auditApplicationId, auditEntryId, queryOptions)).pipe(
catchError((err: any) => this.handleError(err))
);
}
/**
* List audit entries for a node.
*
* @param nodeId The identifier of a node.
* @param opts Options.
* @returns
@@ -137,36 +124,29 @@ export class AuditService {
nodeId
};
const queryOptions = Object.assign({}, defaultOptions, opts);
return from(this.auditApi.listAuditEntriesForNode(queryOptions))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.auditApi.listAuditEntriesForNode(queryOptions)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
* Permanently delete audit entries for an audit application.
*
* @param auditApplicationId The identifier of an audit application.
* @param where Audit entries to permanently delete for an audit application, given an inclusive time period or range of ids.
* @returns
*/
deleteAuditEntries(auditApplicationId: string, where: string): Observable<any> {
return from(this.auditApi.deleteAuditEntriesForAuditApp(auditApplicationId, where))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.auditApi.deleteAuditEntriesForAuditApp(auditApplicationId, where)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
* Permanently delete an audit entry.
*
* @param auditApplicationId The identifier of an audit application.
* @param auditEntryId The identifier of an audit entry.
* @returns
*/
deleteAuditEntry(auditApplicationId: string, auditEntryId: string): Observable<any> {
return from(this.auditApi.deleteAuditEntry(auditApplicationId, auditEntryId))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.auditApi.deleteAuditEntry(auditApplicationId, auditEntryId)).pipe(catchError((err: any) => this.handleError(err)));
}
private handleError(error: any): any {

View File

@@ -129,7 +129,7 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
}
parseRoute(node: Node): PathElement[] {
if (node && node.path) {
if (node?.path) {
const route = (node.path.elements || []).slice();
route.push({

View File

@@ -43,27 +43,27 @@ export class EcmUserModel {
capabilities?: Capabilities;
constructor(obj?: any) {
this.id = obj && obj.id || null;
this.firstName = obj && obj.firstName;
this.lastName = obj && obj.lastName;
this.description = obj && obj.description || null;
this.avatarId = obj && obj.avatarId || null;
this.email = obj && obj.email || null;
this.skypeId = obj && obj.skypeId;
this.googleId = obj && obj.googleId;
this.instantMessageId = obj && obj.instantMessageId;
this.jobTitle = obj && obj.jobTitle || null;
this.location = obj && obj.location || null;
this.company = obj && obj.company;
this.mobile = obj && obj.mobile;
this.telephone = obj && obj.telephone;
this.statusUpdatedAt = obj && obj.statusUpdatedAt;
this.userStatus = obj && obj.userStatus;
this.enabled = obj && obj.enabled;
this.emailNotificationsEnabled = obj && obj.emailNotificationsEnabled;
this.aspectNames = obj && obj.aspectNames;
this.properties = obj && obj.properties;
this.capabilities = obj && obj.capabilities;
this.id = obj?.id || null;
this.firstName = obj?.firstName;
this.lastName = obj?.lastName;
this.description = obj?.description || null;
this.avatarId = obj?.avatarId || null;
this.email = obj?.email || null;
this.skypeId = obj?.skypeId;
this.googleId = obj?.googleId;
this.instantMessageId = obj?.instantMessageId;
this.jobTitle = obj?.jobTitle || null;
this.location = obj?.location || null;
this.company = obj?.company;
this.mobile = obj?.mobile;
this.telephone = obj?.telephone;
this.statusUpdatedAt = obj?.statusUpdatedAt;
this.userStatus = obj?.userStatus;
this.enabled = obj?.enabled;
this.emailNotificationsEnabled = obj?.emailNotificationsEnabled;
this.aspectNames = obj?.aspectNames;
this.properties = obj?.properties;
this.capabilities = obj?.capabilities;
}
isAdmin(): boolean {

View File

@@ -89,7 +89,7 @@ export class ContentService {
(currentPermission) => currentPermission.authorityId === userId
);
if (permissions.length) {
if (permission && permission.startsWith('!')) {
if (permission?.startsWith('!')) {
hasPermissions = !permissions.find((currentPermission) => currentPermission.name === permission.replace('!', ''));
} else {
hasPermissions = !!permissions.find((currentPermission) => currentPermission.name === permission);
@@ -99,7 +99,7 @@ export class ContentService {
hasPermissions = true;
} else if (permission === PermissionsEnum.NOT_CONSUMER) {
hasPermissions = false;
} else if (permission && permission.startsWith('!')) {
} else if (permission?.startsWith('!')) {
hasPermissions = true;
}
}
@@ -117,8 +117,8 @@ export class ContentService {
hasAllowableOperations(node: Node, allowableOperation: AllowableOperationsEnum | string): boolean {
let hasAllowableOperations = false;
if (node && node.allowableOperations) {
if (allowableOperation && allowableOperation.startsWith('!')) {
if (node?.allowableOperations) {
if (allowableOperation?.startsWith('!')) {
hasAllowableOperations = !node.allowableOperations.find(
(currentOperation) => currentOperation === allowableOperation.replace('!', '')
);
@@ -126,7 +126,7 @@ export class ContentService {
hasAllowableOperations = !!node.allowableOperations.find((currentOperation) => currentOperation === allowableOperation);
}
} else {
if (allowableOperation && allowableOperation.startsWith('!')) {
if (allowableOperation?.startsWith('!')) {
hasAllowableOperations = true;
}
}

View File

@@ -221,7 +221,7 @@ export class NodesApiService {
private cleanMetadataFromSemicolon(nodeEntry: NodeEntry): NodeMetadata {
const metadata = {};
if (nodeEntry && nodeEntry.entry.properties) {
if (nodeEntry?.entry.properties) {
for (const key in nodeEntry.entry.properties) {
if (key) {
if (key.indexOf(':') !== -1) {

View File

@@ -135,7 +135,7 @@ export class SitesService {
*/
getSiteNameFromNodePath(node: Node): string {
let siteName = '';
if (node.path && node.path.elements) {
if (node.path?.elements) {
const foundNode = node.path.elements.find((pathNode) => pathNode.nodeType === 'st:site' && pathNode.name !== 'Sites');
siteName = foundNode ? foundNode.name : '';
}

View File

@@ -18,12 +18,7 @@
import { EventEmitter, Injectable } from '@angular/core';
import { Minimatch } from 'minimatch';
import { Subject } from 'rxjs';
import {
FileUploadCompleteEvent,
FileUploadDeleteEvent,
FileUploadErrorEvent,
FileUploadEvent
} from '../events/file.event';
import { FileUploadCompleteEvent, FileUploadDeleteEvent, FileUploadErrorEvent, FileUploadEvent } from '../events/file.event';
import { FileModel, FileUploadProgress, FileUploadStatus } from '../models/file.model';
import { AppConfigService, AlfrescoApiService } from '@alfresco/adf-core';
import { filter } from 'rxjs/operators';
@@ -81,12 +76,11 @@ export class UploadService {
constructor(
protected apiService: AlfrescoApiService,
private appConfigService: AppConfigService,
private discoveryApiService: DiscoveryApiService) {
this.discoveryApiService.ecmProductInfo$.pipe(filter(info => !!info))
.subscribe(({status}) => {
this.isThumbnailGenerationEnabled = status.isThumbnailGenerationEnabled;
});
private discoveryApiService: DiscoveryApiService
) {
this.discoveryApiService.ecmProductInfo$.pipe(filter((info) => !!info)).subscribe(({ status }) => {
this.isThumbnailGenerationEnabled = status.isThumbnailGenerationEnabled;
});
}
clearCache() {
@@ -108,8 +102,17 @@ export class UploadService {
* @returns True if files in the queue are still uploading, false otherwise
*/
isUploading(): boolean {
const finishedFileStates = [FileUploadStatus.Complete, FileUploadStatus.Cancelled, FileUploadStatus.Aborted, FileUploadStatus.Error, FileUploadStatus.Deleted];
return this.queue.reduce((stillUploading: boolean, currentFile: FileModel) => stillUploading || finishedFileStates.indexOf(currentFile.status) === -1, false);
const finishedFileStates = [
FileUploadStatus.Complete,
FileUploadStatus.Cancelled,
FileUploadStatus.Aborted,
FileUploadStatus.Error,
FileUploadStatus.Deleted
];
return this.queue.reduce(
(stillUploading: boolean, currentFile: FileModel) => stillUploading || finishedFileStates.indexOf(currentFile.status) === -1,
false
);
}
/**
@@ -128,9 +131,7 @@ export class UploadService {
* @returns Array of files that were not blocked from upload by the ignore list
*/
addToQueue(...files: FileModel[]): FileModel[] {
const allowedFiles = files.filter((currentFile) =>
this.filterElement(currentFile)
);
const allowedFiles = files.filter((currentFile) => this.filterElement(currentFile));
this.queue = this.queue.concat(allowedFiles);
this.queueChanged.next(this.queue);
return allowedFiles;
@@ -217,7 +218,7 @@ export class UploadService {
opts.renditions = 'doclib';
}
if (file.options && file.options.versioningEnabled !== undefined) {
if (file.options?.versioningEnabled !== undefined) {
opts.versioningEnabled = file.options.versioningEnabled;
}
@@ -240,13 +241,7 @@ export class UploadService {
const nodeBody: NodeBodyCreate = { ...file.options, name: file.name, nodeType: file.options.nodeType };
delete nodeBody['versioningEnabled'];
return this.uploadApi.uploadFile(
file.file,
file.options.path,
file.options.parentId,
nodeBody,
opts
);
return this.uploadApi.uploadFile(file.file, file.options.path, file.options.parentId, nodeBody, opts);
}
}
@@ -259,7 +254,7 @@ export class UploadService {
}
const files = this.queue
.filter(toUpload => !cached.includes(toUpload.name) && toUpload.status === FileUploadStatus.Pending)
.filter((toUpload) => !cached.includes(toUpload.name) && toUpload.status === FileUploadStatus.Pending)
.slice(0, threadsCount);
return files;
@@ -274,13 +269,13 @@ export class UploadService {
.on('abort', () => {
this.onUploadAborted(file);
if (successEmitter) {
successEmitter.emit({value: 'File aborted'});
successEmitter.emit({ value: 'File aborted' });
}
})
.on('error', (err) => {
this.onUploadError(file, err);
if (errorEmitter) {
errorEmitter.emit({value: 'Error file uploaded'});
errorEmitter.emit({ value: 'Error file uploaded' });
}
})
.on('success', (data) => {
@@ -292,17 +287,16 @@ export class UploadService {
this.deleteAbortedNodeVersion(data.entry.id, data.entry.properties['cm:versionLabel']);
}
if (successEmitter) {
successEmitter.emit({value: 'File deleted'});
successEmitter.emit({ value: 'File deleted' });
}
} else {
this.onUploadComplete(file, data);
if (successEmitter) {
successEmitter.emit({value: data});
successEmitter.emit({ value: data });
}
}
})
.catch(() => {
});
.catch(() => {});
return promise;
}
@@ -316,10 +310,7 @@ export class UploadService {
}
}
private onUploadProgress(
file: FileModel,
progress: FileUploadProgress
): void {
private onUploadProgress(file: FileModel, progress: FileUploadProgress): void {
if (file) {
file.progress = progress;
file.status = FileUploadStatus.Progress;
@@ -330,9 +321,9 @@ export class UploadService {
}
}
private onUploadError(file: FileModel, error: any): void {
private onUploadError(file: FileModel, error: { status?: number }): void {
if (file) {
file.errorCode = (error || {}).status;
file.errorCode = error?.status;
file.status = FileUploadStatus.Error;
this.totalError++;
@@ -341,11 +332,7 @@ export class UploadService {
delete this.cache[file.name];
}
const event = new FileUploadErrorEvent(
file,
error,
this.totalError
);
const event = new FileUploadErrorEvent(file, error, this.totalError);
this.fileUpload.next(event);
this.fileUploadError.next(event);
}
@@ -361,12 +348,7 @@ export class UploadService {
delete this.cache[file.name];
}
const event = new FileUploadCompleteEvent(
file,
this.totalComplete,
data,
this.totalAborted
);
const event = new FileUploadCompleteEvent(file, this.totalComplete, data, this.totalAborted);
this.fileUpload.next(event);
this.fileUploadComplete.next(event);
}
@@ -415,20 +397,15 @@ export class UploadService {
}
private deleteAbortedNode(nodeId: string) {
this.nodesApi.deleteNode(nodeId, {permanent: true})
.then(() => (this.abortedFile = undefined));
this.nodesApi.deleteNode(nodeId, { permanent: true }).then(() => (this.abortedFile = undefined));
}
private deleteAbortedNodeVersion(nodeId: string, versionId: string) {
this.versionsApi.deleteVersion(nodeId, versionId)
.then(() => (this.abortedFile = undefined));
this.versionsApi.deleteVersion(nodeId, versionId).then(() => (this.abortedFile = undefined));
}
private isSaveToAbortFile(file: FileModel): boolean {
return (
file.size > MIN_CANCELLABLE_FILE_SIZE &&
file.progress.percent < MAX_CANCELLABLE_FILE_PERCENTAGE
);
return file.size > MIN_CANCELLABLE_FILE_SIZE && file.progress.percent < MAX_CANCELLABLE_FILE_PERCENTAGE;
}
private filterElement(file: FileModel) {
@@ -454,12 +431,12 @@ export class UploadService {
const fileRelativePath = currentFile.webkitRelativePath ? currentFile.webkitRelativePath : file.options.path;
if (currentFile && fileRelativePath) {
isAllowed =
this.excludedFoldersList.filter((folderToExclude) => fileRelativePath
.split('/')
.some((pathElement) => {
this.excludedFoldersList.filter((folderToExclude) =>
fileRelativePath.split('/').some((pathElement) => {
const minimatch = new Minimatch(folderToExclude, this.folderMatchingOptions);
return minimatch.match(pathElement);
})).length === 0;
})
).length === 0;
}
return isAllowed;
}

View File

@@ -31,7 +31,6 @@ import { AllowableOperationsEnum } from '../../../common/models/allowable-operat
host: { class: 'adf-content-metadata-card' }
})
export class ContentMetadataCardComponent implements OnChanges {
/** (required) The node entity to fetch metadata about */
@Input()
node: Node;
@@ -101,12 +100,16 @@ export class ContentMetadataCardComponent implements OnChanges {
editAspectSupported = false;
constructor(private contentService: ContentService, private nodeAspectService: NodeAspectService, private versionCompatibilityService: VersionCompatibilityService) {
constructor(
private contentService: ContentService,
private nodeAspectService: NodeAspectService,
private versionCompatibilityService: VersionCompatibilityService
) {
this.editAspectSupported = this.versionCompatibilityService.isVersionSupported('7');
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.displayAspect && changes.displayAspect.currentValue) {
if (changes.displayAspect?.currentValue) {
this.expanded = true;
}
}

View File

@@ -407,7 +407,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
private isExcludedSiteContent(row: ShareDataRow): boolean {
const entry = row.node.entry;
if (this._excludeSiteContent && this._excludeSiteContent.length && entry && entry.properties && entry.properties['st:componentId']) {
if (this._excludeSiteContent?.length && entry && entry.properties?.['st:componentId']) {
const excludedItem = this._excludeSiteContent.find((id: string) => entry.properties['st:componentId'] === id);
return !!excludedItem;
}
@@ -489,7 +489,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
if (this.customResourcesService.hasCorrespondingNodeIds(this.siteId)) {
this.customResourcesService.getCorrespondingNodeIds(this.siteId).subscribe((nodeIds) => {
if (nodeIds && nodeIds.length) {
if (nodeIds?.length) {
nodeIds
.filter((id) => id !== this.siteId)
.forEach((extraId) => {

View File

@@ -15,14 +15,7 @@
* limitations under the License.
*/
import {
Component,
Inject,
OnInit,
ViewEncapsulation,
ViewChild,
OnDestroy
} from '@angular/core';
import { Component, Inject, OnInit, ViewEncapsulation, ViewChild, OnDestroy } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
import { UntypedFormGroup, UntypedFormControl, AbstractControl } from '@angular/forms';
@@ -43,11 +36,10 @@ type DatePickerType = 'date' | 'time' | 'month' | 'datetime';
selector: 'adf-share-dialog',
templateUrl: './content-node-share.dialog.html',
styleUrls: ['./content-node-share.dialog.scss'],
host: {class: 'adf-share-dialog'},
host: { class: 'adf-share-dialog' },
encapsulation: ViewEncapsulation.None
})
export class ShareDialogComponent implements OnInit, OnDestroy {
minDate = add(new Date(), { days: 1 });
sharedId: string;
fileName: string;
@@ -57,16 +49,16 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
isLinkWithExpiryDate = false;
form: UntypedFormGroup = new UntypedFormGroup({
sharedUrl: new UntypedFormControl(''),
time: new UntypedFormControl({value: '', disabled: true})
time: new UntypedFormControl({ value: '', disabled: true })
});
type: DatePickerType = 'date';
maxDebounceTime = 500;
isExpiryDateToggleChecked: boolean;
@ViewChild('slideToggleExpirationDate', {static: true})
@ViewChild('slideToggleExpirationDate', { static: true })
slideToggleExpirationDate;
@ViewChild('datePickerInput', {static: true})
@ViewChild('datePickerInput', { static: true })
datePickerInput;
private onDestroy$ = new Subject<boolean>();
@@ -78,17 +70,16 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
private contentService: ContentService,
private renditionService: RenditionService,
@Inject(MAT_DIALOG_DATA) public data: ContentNodeShareSettings
) {
}
) {}
ngOnInit() {
if (this.data.node && this.data.node.entry) {
if (this.data.node?.entry) {
this.fileName = this.data.node.entry.name;
this.baseShareUrl = this.data.baseShareUrl;
const properties = this.data.node.entry.properties;
if (!properties || !properties['qshare:sharedId']) {
if (!properties?.['qshare:sharedId']) {
this.createSharedLinks(this.data.node.entry.id);
} else {
this.sharedId = properties['qshare:sharedId'];
@@ -100,12 +91,7 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
}
}
this.time.valueChanges
.pipe(
debounceTime(this.maxDebounceTime),
takeUntil(this.onDestroy$)
)
.subscribe(value => this.onTimeChanged(value));
this.time.valueChanges.pipe(debounceTime(this.maxDebounceTime), takeUntil(this.onDestroy$)).subscribe((value) => this.onTimeChanged(value));
}
onTimeChanged(date: Date) {
@@ -130,9 +116,9 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
}
get canUpdate() {
const {entry} = this.data.node;
const { entry } = this.data.node;
if (entry && entry.allowableOperations) {
if (entry?.allowableOperations) {
return this.contentService.hasAllowableOperations(entry, 'update');
}
@@ -214,28 +200,25 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
deleteSharedLink(sharedId: string, dialogOpenFlag?: boolean) {
this.isDisabled = true;
this.sharedLinksApiService
.deleteSharedLink(sharedId)
.subscribe((response: any) => {
if (response instanceof Error) {
this.isDisabled = false;
this.isFileShared = true;
this.handleError(response);
} else {
if (this.data.node.entry.properties) {
this.data.node.entry.properties['qshare:sharedId'] = null;
this.data.node.entry.properties['qshare:expiryDate'] = null;
}
if (dialogOpenFlag) {
this.createSharedLinks(this.data.node.entry.id);
this.isExpiryDateToggleChecked = false;
this.isLinkWithExpiryDate = false;
} else {
this.dialogRef.close(false);
}
}
this.sharedLinksApiService.deleteSharedLink(sharedId).subscribe((response: any) => {
if (response instanceof Error) {
this.isDisabled = false;
this.isFileShared = true;
this.handleError(response);
} else {
if (this.data.node.entry.properties) {
this.data.node.entry.properties['qshare:sharedId'] = null;
this.data.node.entry.properties['qshare:expiryDate'] = null;
}
);
if (dialogOpenFlag) {
this.createSharedLinks(this.data.node.entry.id);
this.isExpiryDateToggleChecked = false;
this.isLinkWithExpiryDate = false;
} else {
this.dialogRef.close(false);
}
}
});
}
private handleError(error: Error) {
@@ -244,8 +227,7 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
try {
statusCode = JSON.parse(error.message).error.statusCode;
} catch {
}
} catch {}
if (statusCode === 403) {
message = 'SHARE.UNSHARE_PERMISSION_ERROR';
@@ -258,17 +240,20 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
}
private updateForm(): Date {
const {entry} = this.data.node;
const { entry } = this.data.node;
let expiryDate = null;
if (entry && entry.properties) {
if (entry?.properties) {
expiryDate = entry.properties['qshare:expiryDate'];
}
this.form.setValue({
sharedUrl: `${this.baseShareUrl}${this.sharedId}`,
time: expiryDate ? new Date(expiryDate) : null
}, { emitEvent: false });
this.form.setValue(
{
sharedUrl: `${this.baseShareUrl}${this.sharedId}`,
time: expiryDate ? new Date(expiryDate) : null
},
{ emitEvent: false }
);
return expiryDate;
}
@@ -279,25 +264,25 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
if (this.type === 'date') {
expiryDate = format(endOfDay(new Date(date)), `yyyy-MM-dd'T'HH:mm:ss.SSSxx`);
} else {
expiryDate = format((new Date(date)), `yyyy-MM-dd'T'HH:mm:ss.SSSxx`);
expiryDate = format(new Date(date), `yyyy-MM-dd'T'HH:mm:ss.SSSxx`);
}
} else {
expiryDate = null;
}
if (this.sharedId && expiryDate) {
this.isDisabled = true;
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 as Date);
this.isLinkWithExpiryDate = true;
this.updateEntryExpiryDate(date);
}
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 as Date);
this.isLinkWithExpiryDate = true;
this.updateEntryExpiryDate(date);
}
});
}
}
@@ -311,12 +296,10 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
}
private updateEntryExpiryDate(date: Date) {
const {properties} = this.data.node.entry;
const { properties } = this.data.node.entry;
if (properties) {
properties['qshare:expiryDate'] = date
? new Date(date)
: null;
properties['qshare:expiryDate'] = date ? new Date(date) : null;
}
}
}

View File

@@ -29,12 +29,11 @@ import { takeUntil } from 'rxjs/operators';
exportAs: 'adfShare'
})
export class NodeSharedDirective implements OnChanges, OnDestroy {
isFile: boolean = false;
isShared: boolean = false;
/** Node to share. */
// eslint-disable-next-line @angular-eslint/no-input-rename
// eslint-disable-next-line @angular-eslint/no-input-rename
@Input('adf-share')
node: NodeEntry;
@@ -50,11 +49,7 @@ export class NodeSharedDirective implements OnChanges, OnDestroy {
return this._nodesApi;
}
constructor(
private dialog: MatDialog,
private zone: NgZone,
private alfrescoApiService: AlfrescoApiService) {
}
constructor(private dialog: MatDialog, private zone: NgZone, private alfrescoApiService: AlfrescoApiService) {}
ngOnDestroy() {
this.onDestroy$.next(true);
@@ -62,7 +57,7 @@ export class NodeSharedDirective implements OnChanges, OnDestroy {
}
shareNode(nodeEntry: NodeEntry) {
if (nodeEntry && nodeEntry.entry && nodeEntry.entry.isFile) {
if (nodeEntry?.entry?.isFile) {
// shared and favorite
const nodeId = nodeEntry.entry['nodeId'] || nodeEntry.entry['guid'];
@@ -96,14 +91,12 @@ export class NodeSharedDirective implements OnChanges, OnDestroy {
}
ngOnChanges() {
this.zone.onStable
.pipe(takeUntil(this.onDestroy$))
.subscribe(() => {
if (this.node && this.node.entry) {
this.isFile = this.node.entry.isFile;
this.isShared = this.node.entry.properties ? this.node.entry.properties['qshare:sharedId'] : false;
}
});
this.zone.onStable.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
if (this.node?.entry) {
this.isFile = this.node.entry.isFile;
this.isShared = this.node.entry.properties ? this.node.entry.properties['qshare:sharedId'] : false;
}
});
}
@HostListener('click')

View File

@@ -46,7 +46,7 @@ export class DownloadZipDialogComponent implements OnInit {
) {}
ngOnInit() {
if (this.data && this.data.nodeIds && this.data.nodeIds.length > 0) {
if (this.data?.nodeIds?.length > 0) {
if (!this.cancelled) {
this.downloadZip(this.data.nodeIds);
} else {
@@ -64,7 +64,7 @@ export class DownloadZipDialogComponent implements OnInit {
downloadZip(nodeIds: string[]) {
if (nodeIds && nodeIds.length > 0) {
this.downloadZipService.createDownload({ nodeIds }).subscribe((data: DownloadEntry) => {
if (data && data.entry && data.entry.id) {
if (data?.entry?.id) {
const url = this.contentService.getContentUrl(data.entry.id, true);
this.nodeService.getNode(data.entry.id).subscribe((downloadNode) => {

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Directive, HostListener, Input, OnChanges, Output, EventEmitter } from '@angular/core';
import { Directive, HostListener, Input, OnChanges, Output, EventEmitter, SimpleChanges } from '@angular/core';
import { FavoriteBodyCreate, FavoritesApi } from '@alfresco/js-api';
import { AlfrescoApiService } from '@alfresco/adf-core';
import { LibraryEntity } from '../interfaces/library-entity.interface';
@@ -59,7 +59,7 @@ export class LibraryFavoriteDirective implements OnChanges {
constructor(private alfrescoApiService: AlfrescoApiService) {}
ngOnChanges(changes) {
ngOnChanges(changes: SimpleChanges) {
if (!changes.library.currentValue) {
this.targetLibrary = null;
return;
@@ -70,7 +70,7 @@ export class LibraryFavoriteDirective implements OnChanges {
}
isFavorite(): boolean {
return this.targetLibrary && this.targetLibrary.isFavorite;
return this.targetLibrary?.isFavorite;
}
private async markFavoriteLibrary(library: LibraryEntity) {

View File

@@ -16,17 +16,11 @@
*/
import { Directive, EventEmitter, HostListener, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import {
SiteEntry,
SiteMembershipRequestBodyCreate,
SiteMemberEntry,
SiteMembershipRequestEntry,
SitesApi
} from '@alfresco/js-api';
import { SiteEntry, SiteMembershipRequestBodyCreate, SiteMembershipRequestEntry, SitesApi } from '@alfresco/js-api';
import { BehaviorSubject, from, Observable } from 'rxjs';
import { AlfrescoApiService } from '@alfresco/adf-core';
import { LibraryMembershipToggleEvent } from '../interfaces/library-membership-toggle-event.interface';
import { LibraryMembershipErrorEvent} from '../interfaces/library-membership-error-event.interface';
import { LibraryMembershipErrorEvent } from '../interfaces/library-membership-error-event.interface';
import { VersionCompatibilityService } from '../version-compatibility/version-compatibility.service';
import { SitesService } from '../common/services/sites.service';
@@ -39,7 +33,7 @@ export class LibraryMembershipDirective implements OnChanges {
isJoinRequested: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
_sitesApi: SitesApi;
private _sitesApi: SitesApi;
get sitesApi(): SitesApi {
this._sitesApi = this._sitesApi ?? new SitesApi(this.alfrescoApiService.getInstance());
return this._sitesApi;
@@ -69,11 +63,10 @@ export class LibraryMembershipDirective implements OnChanges {
private alfrescoApiService: AlfrescoApiService,
private sitesService: SitesService,
private versionCompatibilityService: VersionCompatibilityService
) {
}
) {}
ngOnChanges(changes: SimpleChanges) {
if (!changes.selection.currentValue || !changes.selection.currentValue.entry) {
if (!changes.selection.currentValue?.entry) {
this.targetSite = null;
return;
@@ -115,7 +108,7 @@ export class LibraryMembershipDirective implements OnChanges {
this.targetSite.joinRequested = true;
this.isJoinRequested.next(true);
if (createdMembership.entry && createdMembership.entry.site && createdMembership.entry.site.role) {
if (createdMembership.entry?.site?.role) {
const info = {
shouldReload: true,
i18nKey: 'ADF_LIBRARY_MEMBERSHIP_MESSAGES.INFO.JOINED'
@@ -154,8 +147,8 @@ export class LibraryMembershipDirective implements OnChanges {
if (this.isAdmin) {
this.joinLibrary().subscribe(
(createdMembership: SiteMemberEntry) => {
if (createdMembership.entry && createdMembership.entry.role) {
(createdMembership) => {
if (createdMembership.entry?.role) {
const info = {
shouldReload: true,
i18nKey: 'ADF_LIBRARY_MEMBERSHIP_MESSAGES.INFO.JOINED'
@@ -223,7 +216,7 @@ export class LibraryMembershipDirective implements OnChanges {
});
}
private cancelJoinRequest() {
private cancelJoinRequest(): Observable<void> {
return from(this.sitesApi.deleteSiteMembershipRequestForPerson('-me-', this.targetSite.id));
}

View File

@@ -78,10 +78,7 @@ export class NodeDeleteDirective implements OnChanges {
this.process(this.selection);
}
constructor(private alfrescoApiService: AlfrescoApiService,
private translation: TranslationService,
private elementRef: ElementRef) {
}
constructor(private alfrescoApiService: AlfrescoApiService, private translation: TranslationService, private elementRef: ElementRef) {}
ngOnChanges() {
if (!this.selection || (this.selection && this.selection.length === 0)) {
@@ -98,23 +95,21 @@ export class NodeDeleteDirective implements OnChanges {
}
private process(selection: NodeEntry[] | DeletedNodeEntry[]) {
if (selection && selection.length) {
if (selection?.length) {
const batch = this.getDeleteNodesBatch(selection);
forkJoin(...batch)
.subscribe((data: ProcessedNodeData[]) => {
const processedItems: ProcessStatus = this.processStatus(data);
const message = this.getMessage(processedItems);
forkJoin(...batch).subscribe((data: ProcessedNodeData[]) => {
const processedItems: ProcessStatus = this.processStatus(data);
const message = this.getMessage(processedItems);
if (message) {
this.delete.emit(message);
}
});
if (message) {
this.delete.emit(message);
}
});
}
}
private getDeleteNodesBatch(selection: any): Observable<ProcessedNodeData>[] {
private getDeleteNodesBatch(selection: NodeEntry[] | DeletedNodeEntry[]): Observable<ProcessedNodeData>[] {
return selection.map((node) => this.deleteNode(node));
}
@@ -135,10 +130,12 @@ export class NodeDeleteDirective implements OnChanges {
entry: node.entry,
status: 1
})),
catchError(() => of({
entry: node.entry,
status: 0
}))
catchError(() =>
of({
entry: node.entry,
status: 0
})
)
);
}
@@ -147,10 +144,10 @@ export class NodeDeleteDirective implements OnChanges {
success: [],
failed: [],
get someFailed() {
return !!(this.failed.length);
return !!this.failed.length;
},
get someSucceeded() {
return !!(this.success.length);
return !!this.success.length;
},
get oneFailed() {
return this.failed.length === 1;
@@ -166,18 +163,15 @@ export class NodeDeleteDirective implements OnChanges {
}
};
return data.reduce(
(acc, next) => {
if (next.status === 1) {
acc.success.push(next);
} else {
acc.failed.push(next);
}
return data.reduce((acc, next) => {
if (next.status === 1) {
acc.success.push(next);
} else {
acc.failed.push(next);
}
return acc;
},
deleteStatus
);
return acc;
}, deleteStatus);
}
private getMessage(status: ProcessStatus): string | null {
@@ -198,37 +192,25 @@ export class NodeDeleteDirective implements OnChanges {
}
if (status.someFailed && status.someSucceeded && !status.oneSucceeded) {
return this.translation.instant(
'CORE.DELETE_NODE.PARTIAL_PLURAL',
{
success: status.success.length,
failed: status.failed.length
}
);
return this.translation.instant('CORE.DELETE_NODE.PARTIAL_PLURAL', {
success: status.success.length,
failed: status.failed.length
});
}
if (status.someFailed && status.oneSucceeded) {
return this.translation.instant(
'CORE.DELETE_NODE.PARTIAL_SINGULAR',
{
success: status.success.length,
failed: status.failed.length
}
);
return this.translation.instant('CORE.DELETE_NODE.PARTIAL_SINGULAR', {
success: status.success.length,
failed: status.failed.length
});
}
if (status.oneFailed && !status.someSucceeded) {
return this.translation.instant(
'CORE.DELETE_NODE.ERROR_SINGULAR',
{ name: status.failed[0].entry.name }
);
return this.translation.instant('CORE.DELETE_NODE.ERROR_SINGULAR', { name: status.failed[0].entry.name });
}
if (status.oneSucceeded && !status.someFailed) {
return this.translation.instant(
'CORE.DELETE_NODE.SINGULAR',
{ name: status.success[0].entry.name }
);
return this.translation.instant('CORE.DELETE_NODE.SINGULAR', { name: status.success[0].entry.name });
}
return null;

View File

@@ -29,7 +29,6 @@ import { ContentApi, NodeEntry, VersionEntry } from '@alfresco/js-api';
selector: '[adfNodeDownload]'
})
export class NodeDownloadDirective {
_contentApi: ContentApi;
get contentApi(): ContentApi {
this._contentApi = this._contentApi ?? new ContentApi(this.apiService.getInstance());
@@ -49,11 +48,7 @@ export class NodeDownloadDirective {
this.downloadNodes(this.nodes);
}
constructor(
private apiService: AlfrescoApiService,
private downloadService: DownloadService,
private dialog: MatDialog) {
}
constructor(private apiService: AlfrescoApiService, private downloadService: DownloadService, private dialog: MatDialog) {}
/**
* Downloads multiple selected nodes.
@@ -62,7 +57,6 @@ export class NodeDownloadDirective {
* @param selection Multiple selected nodes to download
*/
downloadNodes(selection: NodeEntry | Array<NodeEntry>) {
if (!this.isSelectionValid(selection)) {
return;
}
@@ -84,7 +78,7 @@ export class NodeDownloadDirective {
* @param node Node to download
*/
downloadNode(node: NodeEntry) {
if (node && node.entry) {
if (node?.entry) {
const entry = node.entry;
if (entry.isFile) {
@@ -107,12 +101,12 @@ export class NodeDownloadDirective {
}
private downloadFile(node: NodeEntry) {
if (node && node.entry) {
if (node?.entry) {
// nodeId for Shared node
const id = (node.entry as any).nodeId || node.entry.id;
let url;
let fileName;
let url: string;
let fileName: string;
if (this.version) {
url = this.contentApi.getVersionContentUrl(id, this.version.entry.id, true);
fileName = this.version.entry.name;
@@ -128,7 +122,7 @@ export class NodeDownloadDirective {
private downloadZip(selection: Array<NodeEntry>) {
if (selection && selection.length > 0) {
// nodeId for Shared node
const nodeIds = selection.map((node: any) => (node.entry.nodeId || node.entry.id));
const nodeIds = selection.map((node: any) => node.entry.nodeId || node.entry.id);
this.dialog.open(DownloadZipDialogComponent, {
width: '600px',

View File

@@ -130,7 +130,7 @@ export class NodeFavoriteDirective implements OnChanges {
const node: Node | SharedLink = selected.entry;
// ACS 6.x with 'isFavorite' include
if (node && node.hasOwnProperty('isFavorite')) {
if (node?.hasOwnProperty('isFavorite')) {
return of(selected);
}

View File

@@ -19,8 +19,20 @@
/* eslint-disable @typescript-eslint/naming-convention */
import {
AfterContentInit, Component, ContentChild, ElementRef, EventEmitter, HostListener, Input,
OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewChild, ViewEncapsulation
AfterContentInit,
Component,
ContentChild,
ElementRef,
EventEmitter,
HostListener,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
SimpleChanges,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import { ContentService } from '../../common/services/content.service';
@@ -75,15 +87,17 @@ const BYTES_TO_MB_CONVERSION_VALUE = 1048576;
selector: 'adf-document-list',
templateUrl: './document-list.component.html',
styleUrls: ['./document-list.component.scss'],
providers:[{
provide: ADF_DOCUMENT_PARENT_COMPONENT,
useExisting: DocumentListComponent
}, DataTableService],
providers: [
{
provide: ADF_DOCUMENT_PARENT_COMPONENT,
useExisting: DocumentListComponent
},
DataTableService
],
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-document-list' }
})
export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, AfterContentInit, PaginatedComponent, NavigableComponentInterface {
static SINGLE_CLICK_NAVIGATION: string = 'click';
static DOUBLE_CLICK_NAVIGATION: string = 'dblclick';
@@ -94,10 +108,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
totalItems: 0
});
DEFAULT_SORTING: DataSorting[] = [
new DataSorting('name', 'asc'),
new DataSorting('isFolder', 'desc')
];
DEFAULT_SORTING: DataSorting[] = [new DataSorting('name', 'asc'), new DataSorting('isFolder', 'desc')];
@ContentChild(DataColumnListComponent)
columnList: DataColumnListComponent;
@@ -362,34 +373,33 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
return this._nodesApi;
}
constructor(private documentListService: DocumentListService,
private elementRef: ElementRef,
private appConfig: AppConfigService,
private userPreferencesService: UserPreferencesService,
private contentService: ContentService,
private thumbnailService: ThumbnailService,
private alfrescoApiService: AlfrescoApiService,
private nodeService: NodesApiService,
private dataTableService: DataTableService,
private lockService: LockService,
private dialog: MatDialog) {
this.nodeService.nodeUpdated
.pipe(takeUntil(this.onDestroy$))
.subscribe((node) => {
this.dataTableService.rowUpdate.next({id: node.id, obj: {entry: node}});
});
constructor(
private documentListService: DocumentListService,
private elementRef: ElementRef,
private appConfig: AppConfigService,
private userPreferencesService: UserPreferencesService,
private contentService: ContentService,
private thumbnailService: ThumbnailService,
private alfrescoApiService: AlfrescoApiService,
private nodeService: NodesApiService,
private dataTableService: DataTableService,
private lockService: LockService,
private dialog: MatDialog
) {
this.nodeService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node) => {
this.dataTableService.rowUpdate.next({ id: node.id, obj: { entry: node } });
});
this.userPreferencesService
.select(UserPreferenceValues.PaginationSize)
.pipe(takeUntil(this.onDestroy$))
.subscribe(pagSize => {
.subscribe((pagSize) => {
this.maxItems = this._pagination.maxItems = pagSize;
});
}
getContextActions(node: NodeEntry) {
if (node && node.entry) {
if (node?.entry) {
const actions = this.getNodeActions(node);
if (actions && actions.length > 0) {
return actions.map((currentAction: ContentActionModel) => ({
@@ -403,7 +413,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
}
private get hasCustomLayout(): boolean {
return this.columnList && this.columnList.columns && this.columnList.columns.length > 0;
return this.columnList?.columns?.length > 0;
}
private getDefaultSorting(): DataSorting {
@@ -433,8 +443,14 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
ngOnInit() {
this.rowMenuCache = {};
this.loadLayoutPresets();
this.data = new ShareDataTableAdapter(this.thumbnailService, this.contentService, null, this.getDefaultSorting(),
this.sortingMode, this.allowDropFiles);
this.data = new ShareDataTableAdapter(
this.thumbnailService,
this.contentService,
null,
this.getDefaultSorting(),
this.sortingMode,
this.allowDropFiles
);
this.data.thumbnails = this.thumbnails;
this.data.permissionsStyle = this.permissionsStyle;
@@ -446,9 +462,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
this.data.setImageResolver(this.imageResolver);
}
this.contextActionHandler
.pipe(takeUntil(this.onDestroy$))
.subscribe(val => this.contextActionCallback(val));
this.contextActionHandler.pipe(takeUntil(this.onDestroy$)).subscribe((val) => this.contextActionCallback(val));
this.enforceSingleClickNavigationForMobile();
if (this.filterValue && Object.keys(this.filterValue).length > 0) {
@@ -458,9 +472,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
ngAfterContentInit() {
if (this.columnList) {
this.columnList.columns.changes
.pipe(takeUntil(this.onDestroy$))
.subscribe(() => this.setTableSchema());
this.columnList.columns.changes.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.setTableSchema());
}
this.setTableSchema();
}
@@ -517,7 +529,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
}
if (this.data) {
if (changes.node && changes.node.currentValue) {
if (changes.node?.currentValue) {
const merge = this._pagination ? this._pagination.merge : false;
this.data.loadPage(changes.node.currentValue, merge, null);
this.preserveExistingSelection();
@@ -555,7 +567,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
}
getNodeActions(node: NodeEntry | any): ContentActionModel[] {
if (node && node.entry) {
if (node?.entry) {
let target = null;
if (node.entry.isFile) {
@@ -575,9 +587,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
const actionsByTarget = this.actions
.filter((entry) => {
const isVisible = (typeof entry.visible === 'function')
? entry.visible(node)
: entry.visible;
const isVisible = typeof entry.visible === 'function' ? entry.visible(node) : entry.visible;
return isVisible && entry.target.toLowerCase() === target;
})
@@ -613,10 +623,10 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
return action.disabled(node);
}
if ((action.permission &&
action.disableWithNoPermission &&
!this.contentService.hasAllowableOperations(node.entry, action.permission)) ||
this.lockService.isLocked(node.entry)) {
if (
(action.permission && action.disableWithNoPermission && !this.contentService.hasAllowableOperations(node.entry, action.permission)) ||
this.lockService.isLocked(node.entry)
) {
return true;
} else {
return action.disabled;
@@ -654,8 +664,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
}
private isLinkFolder(node: Node) {
return node.nodeType === 'app:folderlink' && node.properties &&
node.properties['cm:destination'];
return node.nodeType === 'app:folderlink' && node.properties && node.properties['cm:destination'];
}
private updateCustomSourceData(nodeId: string): void {
@@ -669,13 +678,11 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
* @param action Action to be executed against the context.
*/
executeContentAction(node: NodeEntry, action: ContentActionModel) {
if (node && node.entry && action) {
const handlerSub = (typeof action.handler === 'function') ? action.handler(node, this, action.permission) : of(true);
if (node?.entry && action) {
const handlerSub = typeof action.handler === 'function' ? action.handler(node, this, action.permission) : of(true);
if (typeof action.execute === 'function' && handlerSub) {
handlerSub
.pipe(takeUntil(this.onDestroy$))
.subscribe(() => action.execute(node));
handlerSub.pipe(takeUntil(this.onDestroy$)).subscribe(() => action.execute(node));
}
}
}
@@ -710,16 +717,18 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
this.updateCustomSourceData(this.currentFolderId);
}
this.documentListService.loadFolderByNodeId(this.currentFolderId, this._pagination, this.includeFields, this.where, this.orderBy)
.subscribe((documentNode: DocumentLoaderNode) => {
this.documentListService.loadFolderByNodeId(this.currentFolderId, this._pagination, this.includeFields, this.where, this.orderBy).subscribe(
(documentNode: DocumentLoaderNode) => {
if (documentNode.currentNode) {
this.folderNode = documentNode.currentNode.entry;
this.$folderNode.next(documentNode.currentNode.entry);
}
this.onPageLoaded(documentNode.children);
}, (err) => {
},
(err) => {
this.handleError(err);
});
}
);
}
resetSelection() {
@@ -751,10 +760,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
}
private buildOrderByArray(currentKey: string, currentDirection: string): string[] {
return [
`${this.additionalSorting.key} ${this.additionalSorting.direction}`,
`${currentKey} ${currentDirection}`
];
return [`${this.additionalSorting.key} ${this.additionalSorting.direction}`, `${currentKey} ${currentDirection}`];
}
/**
@@ -801,7 +807,6 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
this.executeActionClick(nodeEntry);
}
}
}
onNodeDblClick(nodeEntry: NodeEntry) {
@@ -825,7 +830,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
}
executeActionClick(nodeEntry: NodeEntry) {
if (nodeEntry && nodeEntry.entry) {
if (nodeEntry?.entry) {
if (nodeEntry.entry.isFile) {
this.onPreviewFile(nodeEntry);
}
@@ -839,10 +844,9 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
include: this.includeFields
};
this.nodesApi.getNode(nodeEntry.entry['guid'], options)
.then((node: NodeEntry) => {
this.navigateTo(node.entry);
});
this.nodesApi.getNode(nodeEntry.entry['guid'], options).then((node: NodeEntry) => {
this.navigateTo(node.entry);
});
}
}
}
@@ -911,7 +915,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
canNavigateFolder(node: Node): boolean {
let canNavigateFolder: boolean = false;
if (node && node.isFolder) {
if (node?.isFolder) {
canNavigateFolder = true;
}
@@ -960,8 +964,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
if (JSON.parse(err.message).error.statusCode === 403) {
this.noPermission = true;
}
} catch (error) {
}
} catch (error) {}
}
this.setLoadingState(false);
this.error.emit(err);
@@ -976,7 +979,11 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
}
getSelectionBasedOnSelectionMode(): DataRow[] {
return this.hasPreselectedRows() ? (this.isSingleSelectionMode() ? [this.preselectedRows[0]] : this.data.getSelectedRows()) : this.data.getSelectedRows();
return this.hasPreselectedRows()
? this.isSingleSelectionMode()
? [this.preselectedRows[0]]
: this.data.getSelectedRows()
: this.data.getSelectedRows();
}
onPreselectNodes() {

View File

@@ -63,7 +63,7 @@ export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy {
}
ngOnChanges(changes: SimpleChanges) {
if (changes['currentFolderId'] && changes['currentFolderId'].currentValue) {
if (changes['currentFolderId']?.currentValue) {
this.resetFilterHeader();
this.configureSearchParent(changes['currentFolderId'].currentValue);
}

View File

@@ -15,15 +15,7 @@
* limitations under the License.
*/
import {
Component,
ChangeDetectionStrategy,
ViewEncapsulation,
OnInit,
Input,
ElementRef,
OnDestroy
} from '@angular/core';
import { Component, ChangeDetectionStrategy, ViewEncapsulation, OnInit, Input, ElementRef, OnDestroy } from '@angular/core';
import { NodeEntry, Site } from '@alfresco/js-api';
import { ShareDataRow } from '../../data/share-data-row.model';
import { NodesApiService } from '../../../common/services/nodes-api.service';
@@ -36,13 +28,17 @@ import { takeUntil } from 'rxjs/operators';
template: `
<span
role="link"
[attr.aria-label]="'NAME_COLUMN_LINK.ACCESSIBILITY.ARIA_LABEL' | translate:{
name: displayText$ | async
}"
[attr.aria-label]="
'NAME_COLUMN_LINK.ACCESSIBILITY.ARIA_LABEL'
| translate
: {
name: displayText$ | async
}
"
class="adf-datatable-cell-value"
title="{{ displayTooltip$ | async }}"
(click)="onClick()">
(click)="onClick()"
>
{{ displayText$ | async }}
</span>
`,
@@ -62,36 +58,29 @@ export class LibraryNameColumnComponent implements OnInit, OnDestroy {
private onDestroy$ = new Subject<boolean>();
constructor(
private element: ElementRef,
private nodesApiService: NodesApiService
) {}
constructor(private element: ElementRef, private nodesApiService: NodesApiService) {}
ngOnInit() {
this.updateValue();
this.nodesApiService.nodeUpdated
.pipe(takeUntil(this.onDestroy$))
.subscribe(node => {
const row: ShareDataRow = this.context.row;
if (row) {
const { entry } = row.node;
this.nodesApiService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node) => {
const row: ShareDataRow = this.context.row;
if (row) {
const { entry } = row.node;
if (entry === node) {
row.node = { entry };
this.updateValue();
}
if (entry === node) {
row.node = { entry };
this.updateValue();
}
});
}
});
}
protected updateValue() {
this.node = this.context.row.node;
const rows: Array<ShareDataRow> = this.context.data.rows || [];
if (this.node && this.node.entry) {
this.displayText$.next(
this.makeLibraryTitle(this.node.entry as any, rows)
);
if (this.node?.entry) {
this.displayText$.next(this.makeLibraryTitle(this.node.entry as any, rows));
this.displayTooltip$.next(this.makeLibraryTooltip(this.node.entry));
}
}

View File

@@ -15,14 +15,7 @@
* limitations under the License.
*/
import {
Component,
OnInit,
Input,
ChangeDetectionStrategy,
ViewEncapsulation,
OnDestroy
} from '@angular/core';
import { Component, OnInit, Input, ChangeDetectionStrategy, ViewEncapsulation, OnDestroy } from '@angular/core';
import { BehaviorSubject, Subject } from 'rxjs';
import { SiteEntry, Site } from '@alfresco/js-api';
import { ShareDataRow } from '../../data/share-data-row.model';
@@ -32,8 +25,8 @@ import { NodesApiService } from '../../../common/services/nodes-api.service';
@Component({
selector: 'adf-library-role-column',
template: `
<span class="adf-datatable-cell-value" title="{{ (displayText$ | async) | translate }}">
{{ (displayText$ | async) | translate }}
<span class="adf-datatable-cell-value" title="{{ displayText$ | async | translate }}">
{{ displayText$ | async | translate }}
</span>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -53,24 +46,22 @@ export class LibraryRoleColumnComponent implements OnInit, OnDestroy {
ngOnInit() {
this.updateValue();
this.nodesApiService.nodeUpdated
.pipe(takeUntil(this.onDestroy$))
.subscribe(node => {
const row: ShareDataRow = this.context.row;
if (row) {
const { entry } = row.node;
this.nodesApiService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node) => {
const row: ShareDataRow = this.context.row;
if (row) {
const { entry } = row.node;
if (entry === node) {
row.node = { entry };
this.updateValue();
}
if (entry === node) {
row.node = { entry };
this.updateValue();
}
});
}
});
}
protected updateValue() {
const node: SiteEntry = this.context.row.node;
if (node && node.entry) {
if (node?.entry) {
const role: string = node.entry.role;
switch (role) {
case Site.RoleEnum.SiteManager:

View File

@@ -25,8 +25,8 @@ import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'adf-library-status-column',
template: `
<span class="adf-datatable-cell-value" title="{{ (displayText$ | async) | translate }}">
{{ (displayText$ | async) | translate }}
<span class="adf-datatable-cell-value" title="{{ displayText$ | async | translate }}">
{{ displayText$ | async | translate }}
</span>
`,
host: { class: 'adf-library-status-column adf-datatable-content-cell' }
@@ -44,24 +44,22 @@ export class LibraryStatusColumnComponent implements OnInit, OnDestroy {
ngOnInit() {
this.updateValue();
this.nodesApiService.nodeUpdated
.pipe(takeUntil(this.onDestroy$))
.subscribe(node => {
const row: ShareDataRow = this.context.row;
if (row) {
const { entry } = row.node;
this.nodesApiService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node) => {
const row: ShareDataRow = this.context.row;
if (row) {
const { entry } = row.node;
if (entry === node) {
row.node = { entry };
this.updateValue();
}
if (entry === node) {
row.node = { entry };
this.updateValue();
}
});
}
});
}
protected updateValue() {
const node: SiteEntry = this.context.row.node;
if (node && node.entry) {
if (node?.entry) {
const visibility: string = node.entry.visibility;
switch (visibility) {

View File

@@ -15,15 +15,7 @@
* limitations under the License.
*/
import {
Component,
Input,
OnInit,
ChangeDetectionStrategy,
ViewEncapsulation,
ElementRef,
OnDestroy
} from '@angular/core';
import { Component, Input, OnInit, ChangeDetectionStrategy, ViewEncapsulation, ElementRef, OnDestroy } from '@angular/core';
import { NodeEntry } from '@alfresco/js-api';
import { BehaviorSubject, Subject } from 'rxjs';
import { NodesApiService } from '../../../common/services/nodes-api.service';
@@ -35,13 +27,17 @@ import { takeUntil } from 'rxjs/operators';
template: `
<span
role="link"
[attr.aria-label]="'NAME_COLUMN_LINK.ACCESSIBILITY.ARIA_LABEL' | translate:{
name: displayText$ | async
}"
[attr.aria-label]="
'NAME_COLUMN_LINK.ACCESSIBILITY.ARIA_LABEL'
| translate
: {
name: displayText$ | async
}
"
class="adf-datatable-cell-value"
title="{{ node | adfNodeNameTooltip }}"
(click)="onClick()">
(click)="onClick()"
>
{{ displayText$ | async }}
</span>
`,
@@ -66,25 +62,23 @@ export class NameColumnComponent implements OnInit, OnDestroy {
ngOnInit() {
this.updateValue();
this.nodesApiService.nodeUpdated
.pipe(takeUntil(this.onDestroy$))
.subscribe(node => {
const row: ShareDataRow = this.context.row;
if (row) {
const { entry } = row.node;
this.nodesApiService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node) => {
const row: ShareDataRow = this.context.row;
if (row) {
const { entry } = row.node;
if (entry === node) {
row.node = { entry };
this.updateValue();
}
if (entry === node) {
row.node = { entry };
this.updateValue();
}
});
}
});
}
protected updateValue() {
this.node = this.context.row.node;
if (this.node && this.node.entry) {
if (this.node?.entry) {
const displayText = this.context.row.getValue(this.key);
this.displayText$.next(displayText || this.node.entry.id);
}

View File

@@ -15,71 +15,62 @@
* limitations under the License.
*/
import {
Component,
ChangeDetectionStrategy,
ViewEncapsulation,
OnInit,
Input
} from '@angular/core';
import { Component, ChangeDetectionStrategy, ViewEncapsulation, OnInit, Input } from '@angular/core';
import { NodeEntry } from '@alfresco/js-api';
import { ShareDataRow } from '../../data/share-data-row.model';
@Component({
selector: 'adf-trashcan-name-column',
template: `
<ng-container *ngIf="!isLibrary">
<span class="adf-datatable-cell-value" title="{{ node | adfNodeNameTooltip }}">{{ displayText }}</span>
</ng-container>
<ng-container *ngIf="isLibrary">
<span class="adf-datatable-cell-value" title="{{ displayTooltip }}">{{ displayText }}</span>
</ng-container>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-datatable-content-cell adf-trashcan-name-column' }
selector: 'adf-trashcan-name-column',
template: `
<ng-container *ngIf="!isLibrary">
<span class="adf-datatable-cell-value" title="{{ node | adfNodeNameTooltip }}">{{ displayText }}</span>
</ng-container>
<ng-container *ngIf="isLibrary">
<span class="adf-datatable-cell-value" title="{{ displayTooltip }}">{{ displayText }}</span>
</ng-container>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-datatable-content-cell adf-trashcan-name-column' }
})
export class TrashcanNameColumnComponent implements OnInit {
@Input()
context: any;
@Input()
context: any;
isLibrary = false;
displayText: string;
displayTooltip: string;
node: NodeEntry;
isLibrary = false;
displayText: string;
displayTooltip: string;
node: NodeEntry;
ngOnInit() {
this.node = this.context.row.node;
const rows: Array<ShareDataRow> = this.context.data.rows || [];
ngOnInit() {
this.node = this.context.row.node;
const rows: Array<ShareDataRow> = this.context.data.rows || [];
if (this.node && this.node.entry) {
this.isLibrary = this.node.entry.nodeType === 'st:site';
if (this.node?.entry) {
this.isLibrary = this.node.entry.nodeType === 'st:site';
if (this.isLibrary) {
const { properties } = this.node.entry;
if (this.isLibrary) {
const { properties } = this.node.entry;
this.displayText = this.makeLibraryTitle(this.node.entry, rows);
this.displayTooltip =
properties['cm:description'] || properties['cm:title'];
} else {
this.displayText = this.node.entry.name || this.node.entry.id;
}
}
}
makeLibraryTitle(library: any, rows: Array<ShareDataRow>): string {
const entries = rows.map((r: ShareDataRow) => r.node.entry);
const { id } = library;
const title = library.properties['cm:title'];
let isDuplicate = false;
if (entries) {
isDuplicate = entries.some((entry: any) => entry.id !== id && entry.properties['cm:title'] === title);
this.displayText = this.makeLibraryTitle(this.node.entry, rows);
this.displayTooltip = properties['cm:description'] || properties['cm:title'];
} else {
this.displayText = this.node.entry.name || this.node.entry.id;
}
}
}
return isDuplicate
? `${library.properties['cm:title']} (${library.name})`
: `${library.properties['cm:title']}`;
}
makeLibraryTitle(library: any, rows: Array<ShareDataRow>): string {
const entries = rows.map((r: ShareDataRow) => r.node.entry);
const { id } = library;
const title = library.properties['cm:title'];
let isDuplicate = false;
if (entries) {
isDuplicate = entries.some((entry: any) => entry.id !== id && entry.properties['cm:title'] === title);
}
return isDuplicate ? `${library.properties['cm:title']} (${library.name})` : `${library.properties['cm:title']}`;
}
}

View File

@@ -95,11 +95,11 @@ export class ShareDataRow implements DataRow {
}
isFile(nodeEntry: NodeEntry): boolean {
return nodeEntry.entry && nodeEntry.entry.isFile;
return nodeEntry.entry?.isFile;
}
isFolder(nodeEntry: NodeEntry): boolean {
return nodeEntry.entry && nodeEntry.entry.isFolder;
return nodeEntry.entry?.isFolder;
}
cacheValue(key: string, value: any): any {

View File

@@ -87,7 +87,7 @@ export class DocumentListService implements DocumentListLoader {
*/
getFolder(folder: string, opts?: any, includeFields: string[] = []): Observable<NodePaging> {
let rootNodeId = ROOT_ID;
if (opts && opts.rootFolderId) {
if (opts?.rootFolderId) {
rootNodeId = opts.rootFolderId;
}

View File

@@ -51,7 +51,7 @@ export class SearchPermissionConfigurationService implements SearchConfiguration
private getQuery(searchTerm: string) {
let query: string;
if (this.queryProvider && this.queryProvider.query) {
if (this.queryProvider?.query) {
query = this.queryProvider.query.replace(new RegExp(/\${([^}]+)}/g), searchTerm);
} else {
query = `(email:*${searchTerm}* OR firstName:*${searchTerm}* OR lastName:*${searchTerm}* OR displayName:*${searchTerm}* OR authorityName:*${searchTerm}* OR authorityDisplayName:*${searchTerm}*) AND ANAME:(\"0/APP.DEFAULT\")`;

View File

@@ -22,7 +22,6 @@ import { NodeEntry } from '@alfresco/js-api';
name: 'adfNodeNameTooltip'
})
export class NodeNameTooltipPipe implements PipeTransform {
transform(node: NodeEntry): string {
if (node) {
return this.getNodeTooltip(node);
@@ -46,18 +45,17 @@ export class NodeNameTooltipPipe implements PipeTransform {
}
private getNodeTooltip(node: NodeEntry): string {
if (!node || !node.entry) {
if (!node?.entry) {
return null;
}
const { entry: { properties, name } } = node;
const lines = [ name ];
const {
entry: { properties, name }
} = node;
const lines = [name];
if (properties) {
const {
'cm:title': title,
'cm:description': description
} = properties;
const { 'cm:title': title, 'cm:description': description } = properties;
if (title && description) {
lines[0] = title;

View File

@@ -16,8 +16,19 @@
*/
import { AuthenticationService, ThumbnailService, SearchTextInputComponent } from '@alfresco/adf-core';
import { Component, EventEmitter, Input, OnDestroy, Output,
QueryList, ViewEncapsulation, ViewChild, ViewChildren, TemplateRef, ContentChild } from '@angular/core';
import {
Component,
EventEmitter,
Input,
OnDestroy,
Output,
QueryList,
ViewEncapsulation,
ViewChild,
ViewChildren,
TemplateRef,
ContentChild
} from '@angular/core';
import { NodeEntry } from '@alfresco/js-api';
import { Subject } from 'rxjs';
import { SearchComponent } from './search.component';
@@ -32,7 +43,6 @@ import { EmptySearchResultComponent } from './empty-search-result.component';
host: { class: 'adf-search-control' }
})
export class SearchControlComponent implements OnDestroy {
/** Toggles highlighting of the search term in the results. */
@Input()
highlight: boolean = false;
@@ -90,15 +100,12 @@ export class SearchControlComponent implements OnDestroy {
emptySearchTemplate: EmptySearchResultComponent;
focusSubject = new Subject<FocusEvent>();
noSearchResultTemplate: TemplateRef <any> = null;
noSearchResultTemplate: TemplateRef<any> = null;
searchTerm: string = '';
private onDestroy$ = new Subject<boolean>();
constructor(
public authService: AuthenticationService,
private thumbnailService: ThumbnailService
) {}
constructor(public authService: AuthenticationService, private thumbnailService: ThumbnailService) {}
isNoSearchTemplatePresent(): boolean {
return !!this.emptySearchTemplate;
@@ -126,7 +133,7 @@ export class SearchControlComponent implements OnDestroy {
getMimeType(node: NodeEntry): string {
let mimeType: string;
if (node.entry.content && node.entry.content.mimeType) {
if (node.entry.content?.mimeType) {
mimeType = node.entry.content.mimeType;
}
if (node.entry.isFolder) {
@@ -154,7 +161,7 @@ export class SearchControlComponent implements OnDestroy {
}
onSelectFirstResult() {
if ( this.listResultElement && this.listResultElement.length > 0) {
if (this.listResultElement && this.listResultElement.length > 0) {
const firstElement = this.listResultElement.first as MatListItem;
// eslint-disable-next-line no-underscore-dangle
firstElement._getHostElement().focus();
@@ -184,7 +191,7 @@ export class SearchControlComponent implements OnDestroy {
}
private isListElement(event: any): boolean {
return event.relatedTarget && event.relatedTarget.children[0] && event.relatedTarget.children[0].className === 'mat-list-item-content';
return event.relatedTarget?.children[0] && event.relatedTarget.children[0].className === 'mat-list-item-content';
}
private getNextElementSibling(node: Element): Element {

View File

@@ -18,12 +18,7 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core';
import {
MOMENT_DATE_FORMATS,
MomentDateAdapter,
UserPreferencesService,
UserPreferenceValues
} from '@alfresco/adf-core';
import { MOMENT_DATE_FORMATS, MomentDateAdapter, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core';
import { SearchWidget } from '../../models/search-widget.interface';
import { SearchWidgetSettings } from '../../models/search-widget-settings.interface';
@@ -54,7 +49,6 @@ const DEFAULT_FORMAT_DATE: string = 'DD/MM/YYYY';
host: { class: 'adf-search-date-range' }
})
export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy {
from: UntypedFormControl;
to: UntypedFormControl;
@@ -74,23 +68,28 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy
private onDestroy$ = new Subject<boolean>();
constructor(private dateAdapter: DateAdapter<Moment>,
private userPreferencesService: UserPreferencesService) {
}
constructor(private dateAdapter: DateAdapter<Moment>, private userPreferencesService: UserPreferencesService) {}
getFromValidationMessage(): string {
return this.from.hasError('invalidOnChange') || this.hasParseError(this.from) ? 'SEARCH.FILTER.VALIDATION.INVALID-DATE' :
this.from.hasError('matDatepickerMax') ? 'SEARCH.FILTER.VALIDATION.BEYOND-MAX-DATE' :
this.from.hasError('required') ? 'SEARCH.FILTER.VALIDATION.REQUIRED-VALUE' :
'';
return this.from.hasError('invalidOnChange') || this.hasParseError(this.from)
? 'SEARCH.FILTER.VALIDATION.INVALID-DATE'
: this.from.hasError('matDatepickerMax')
? 'SEARCH.FILTER.VALIDATION.BEYOND-MAX-DATE'
: this.from.hasError('required')
? 'SEARCH.FILTER.VALIDATION.REQUIRED-VALUE'
: '';
}
getToValidationMessage(): string {
return this.to.hasError('invalidOnChange') || this.hasParseError(this.to) ? 'SEARCH.FILTER.VALIDATION.INVALID-DATE' :
this.to.hasError('matDatepickerMin') ? 'SEARCH.FILTER.VALIDATION.NO-DAYS' :
this.to.hasError('matDatepickerMax') ? 'SEARCH.FILTER.VALIDATION.BEYOND-MAX-DATE' :
this.to.hasError('required') ? 'SEARCH.FILTER.VALIDATION.REQUIRED-VALUE' :
'';
return this.to.hasError('invalidOnChange') || this.hasParseError(this.to)
? 'SEARCH.FILTER.VALIDATION.INVALID-DATE'
: this.to.hasError('matDatepickerMin')
? 'SEARCH.FILTER.VALIDATION.NO-DAYS'
: this.to.hasError('matDatepickerMax')
? 'SEARCH.FILTER.VALIDATION.BEYOND-MAX-DATE'
: this.to.hasError('required')
? 'SEARCH.FILTER.VALIDATION.REQUIRED-VALUE'
: '';
}
ngOnInit() {
@@ -102,13 +101,11 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy
this.userPreferencesService
.select(UserPreferenceValues.Locale)
.pipe(takeUntil(this.onDestroy$))
.subscribe(locale => this.setLocale(locale));
.subscribe((locale) => this.setLocale(locale));
const validators = Validators.compose([
Validators.required
]);
const validators = Validators.compose([Validators.required]);
if (this.settings && this.settings.maxDate) {
if (this.settings?.maxDate) {
if (this.settings.maxDate === 'today') {
this.maxDate = this.dateAdapter.today().endOf('day');
} else {
@@ -174,7 +171,12 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy
if (this.form.invalid || this.form.pristine) {
this.displayValue$.next('');
} else {
this.displayValue$.next(`${this.dateAdapter.format(this.form.value.from, this.datePickerFormat)} - ${this.dateAdapter.format(this.form.value.to, this.datePickerFormat)}`);
this.displayValue$.next(
`${this.dateAdapter.format(this.form.value.from, this.datePickerFormat)} - ${this.dateAdapter.format(
this.form.value.to,
this.datePickerFormat
)}`
);
}
}
@@ -219,10 +221,9 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy
}
onChangedHandler(event: any, formControl: UntypedFormControl) {
const inputValue = event.value;
const formatDate = this.dateAdapter.parse(inputValue, this.datePickerFormat);
if (formatDate && formatDate.isValid()) {
if (formatDate?.isValid()) {
formControl.setValue(formatDate);
} else if (formatDate) {
formControl.setErrors({
@@ -247,6 +248,6 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy
}
setFromMaxDate() {
this.fromMaxDate = (!this.to.value || this.maxDate && (moment(this.maxDate).isBefore(this.to.value))) ? this.maxDate : moment(this.to.value);
this.fromMaxDate = !this.to.value || (this.maxDate && moment(this.maxDate).isBefore(this.to.value)) ? this.maxDate : moment(this.to.value);
}
}

View File

@@ -42,14 +42,11 @@ const DEFAULT_DATETIME_FORMAT: string = 'DD/MM/YYYY HH:mm';
selector: 'adf-search-datetime-range',
templateUrl: './search-datetime-range.component.html',
styleUrls: ['./search-datetime-range.component.scss'],
providers: [
{ provide: MAT_DATETIME_FORMATS, useValue: MAT_MOMENT_DATETIME_FORMATS }
],
providers: [{ provide: MAT_DATETIME_FORMATS, useValue: MAT_MOMENT_DATETIME_FORMATS }],
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-search-date-range' }
})
export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDestroy {
from: UntypedFormControl;
to: UntypedFormControl;
@@ -69,23 +66,28 @@ export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDes
private onDestroy$ = new Subject<boolean>();
constructor(private dateAdapter: DatetimeAdapter<Moment>,
private userPreferencesService: UserPreferencesService) {
}
constructor(private dateAdapter: DatetimeAdapter<Moment>, private userPreferencesService: UserPreferencesService) {}
getFromValidationMessage(): string {
return this.from.hasError('invalidOnChange') || this.hasParseError(this.from) ? 'SEARCH.FILTER.VALIDATION.INVALID-DATETIME' :
this.from.hasError('matDatepickerMax') ? 'SEARCH.FILTER.VALIDATION.BEYOND-MAX-DATETIME' :
this.from.hasError('required') ? 'SEARCH.FILTER.VALIDATION.REQUIRED-VALUE' :
'';
return this.from.hasError('invalidOnChange') || this.hasParseError(this.from)
? 'SEARCH.FILTER.VALIDATION.INVALID-DATETIME'
: this.from.hasError('matDatepickerMax')
? 'SEARCH.FILTER.VALIDATION.BEYOND-MAX-DATETIME'
: this.from.hasError('required')
? 'SEARCH.FILTER.VALIDATION.REQUIRED-VALUE'
: '';
}
getToValidationMessage(): string {
return this.to.hasError('invalidOnChange') || this.hasParseError(this.to) ? 'SEARCH.FILTER.VALIDATION.INVALID-DATETIME' :
this.to.hasError('matDatepickerMin') ? 'SEARCH.FILTER.VALIDATION.NO-DAYS' :
this.to.hasError('matDatepickerMax') ? 'SEARCH.FILTER.VALIDATION.BEYOND-MAX-DATETIME' :
this.to.hasError('required') ? 'SEARCH.FILTER.VALIDATION.REQUIRED-VALUE' :
'';
return this.to.hasError('invalidOnChange') || this.hasParseError(this.to)
? 'SEARCH.FILTER.VALIDATION.INVALID-DATETIME'
: this.to.hasError('matDatepickerMin')
? 'SEARCH.FILTER.VALIDATION.NO-DAYS'
: this.to.hasError('matDatepickerMax')
? 'SEARCH.FILTER.VALIDATION.BEYOND-MAX-DATETIME'
: this.to.hasError('required')
? 'SEARCH.FILTER.VALIDATION.REQUIRED-VALUE'
: '';
}
ngOnInit() {
@@ -94,13 +96,11 @@ export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDes
this.userPreferencesService
.select(UserPreferenceValues.Locale)
.pipe(takeUntil(this.onDestroy$))
.subscribe(locale => this.setLocale(locale));
.subscribe((locale) => this.setLocale(locale));
const validators = Validators.compose([
Validators.required
]);
const validators = Validators.compose([Validators.required]);
if (this.settings && this.settings.maxDatetime) {
if (this.settings?.maxDatetime) {
this.maxDatetime = moment(this.settings.maxDatetime);
}
@@ -161,7 +161,12 @@ export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDes
if (this.form.invalid || this.form.pristine) {
this.displayValue$.next('');
} else {
this.displayValue$.next(`${this.dateAdapter.format(this.form.value.from, this.datetimePickerFormat)} - ${this.dateAdapter.format(this.form.value.to, this.datetimePickerFormat)}`);
this.displayValue$.next(
`${this.dateAdapter.format(this.form.value.from, this.datetimePickerFormat)} - ${this.dateAdapter.format(
this.form.value.to,
this.datetimePickerFormat
)}`
);
}
}
@@ -207,10 +212,9 @@ export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDes
}
onChangedHandler(event: any, formControl: UntypedFormControl) {
const inputValue = event.value;
const formatDate = this.dateAdapter.parse(inputValue, this.datetimePickerFormat);
if (formatDate && formatDate.isValid()) {
if (formatDate?.isValid()) {
formControl.setValue(formatDate);
} else if (formatDate) {
formControl.setErrors({
@@ -235,6 +239,7 @@ export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDes
}
setFromMaxDatetime() {
this.fromMaxDatetime = (!this.to.value || this.maxDatetime && (moment(this.maxDatetime).isBefore(this.to.value))) ? this.maxDatetime : moment(this.to.value);
this.fromMaxDatetime =
!this.to.value || (this.maxDatetime && moment(this.maxDatetime).isBefore(this.to.value)) ? this.maxDatetime : moment(this.to.value);
}
}

View File

@@ -33,19 +33,19 @@ import { Subject } from 'rxjs';
encapsulation: ViewEncapsulation.None
})
export class SearchFacetFieldComponent implements FacetWidget {
@Input()
field!: FacetField;
displayValue$: Subject<string> = new Subject<string>();
constructor(@Inject(SEARCH_QUERY_SERVICE_TOKEN) public queryBuilder: SearchQueryBuilderService,
private searchFacetFiltersService: SearchFacetFiltersService,
private translationService: TranslationService) {
}
constructor(
@Inject(SEARCH_QUERY_SERVICE_TOKEN) public queryBuilder: SearchQueryBuilderService,
private searchFacetFiltersService: SearchFacetFiltersService,
private translationService: TranslationService
) {}
get canUpdateOnChange() {
return this.field.settings?.allowUpdateOnChange ?? true;
return this.field.settings?.allowUpdateOnChange ?? true;
}
onToggleBucket(event: MatCheckboxChange, field: FacetField, bucket: FacetFieldBucket) {
@@ -83,14 +83,14 @@ export class SearchFacetFieldComponent implements FacetWidget {
}
canResetSelectedBuckets(field: FacetField): boolean {
if (field && field.buckets) {
if (field?.buckets) {
return field.buckets.items.some((bucket) => bucket.checked);
}
return false;
}
resetSelectedBuckets(field: FacetField) {
if (field && field.buckets) {
if (field?.buckets) {
for (const bucket of field.buckets.items) {
bucket.checked = false;
this.queryBuilder.removeUserFacetBucket(field.field, bucket);
@@ -110,7 +110,8 @@ export class SearchFacetFieldComponent implements FacetWidget {
if (!this.field.buckets?.items) {
this.displayValue$.next('');
} else {
const displayValue = this.field.buckets?.items?.filter((item) => item.checked)
const displayValue = this.field.buckets?.items
?.filter((item) => item.checked)
.map((item) => this.translationService.instant(item.display || item.label))
.join(', ');
this.displayValue$.next(displayValue);

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Component, Inject, Input, ViewEncapsulation } from '@angular/core';
import { Component, Inject, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { SearchFacetFiltersService } from '../../services/search-facet-filters.service';
import { SEARCH_QUERY_SERVICE_TOKEN } from '../../search-query-service.token';
import { SearchQueryBuilderService } from '../../services/search-query-builder.service';
@@ -28,7 +28,7 @@ import { takeUntil } from 'rxjs/operators';
styleUrls: ['./search-filter-chips.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class SearchFilterChipsComponent {
export class SearchFilterChipsComponent implements OnInit, OnDestroy {
private onDestroy$ = new Subject<void>();
/** Toggles whether to show or not the context facet filters. */
@@ -40,12 +40,14 @@ export class SearchFilterChipsComponent {
constructor(
@Inject(SEARCH_QUERY_SERVICE_TOKEN)
public queryBuilder: SearchQueryBuilderService,
public facetFiltersService: SearchFacetFiltersService) {}
public facetFiltersService: SearchFacetFiltersService
) {}
ngOnInit() {
this.queryBuilder.executed.asObservable()
.pipe(takeUntil(this.onDestroy$))
.subscribe(() => this.facetChipTabbedId = 'search-fact-chip-tabbed-' + this.facetFiltersService.tabbedFacet?.fields.join('-'));
this.queryBuilder.executed
.asObservable()
.pipe(takeUntil(this.onDestroy$))
.subscribe(() => (this.facetChipTabbedId = 'search-fact-chip-tabbed-' + this.facetFiltersService.tabbedFacet?.fields.join('-')));
}
ngOnDestroy() {

View File

@@ -15,18 +15,7 @@
* limitations under the License.
*/
import {
Component,
Input,
Output,
OnInit,
EventEmitter,
ViewEncapsulation,
ViewChild,
Inject,
OnDestroy,
ElementRef
} from '@angular/core';
import { Component, Input, Output, OnInit, EventEmitter, ViewEncapsulation, ViewChild, Inject, OnDestroy, ElementRef } from '@angular/core';
import { ConfigurableFocusTrapFactory, ConfigurableFocusTrap } from '@angular/cdk/a11y';
import { DataColumn, TranslationService } from '@alfresco/adf-core';
import { SearchWidgetContainerComponent } from '../search-widget-container/search-widget-container.component';
@@ -44,7 +33,6 @@ import { FilterSearch } from '../../models/filter-search.interface';
encapsulation: ViewEncapsulation.None
})
export class SearchFilterContainerComponent implements OnInit, OnDestroy {
/** The column the filter will be applied on. */
@Input()
col: DataColumn;
@@ -69,14 +57,15 @@ export class SearchFilterContainerComponent implements OnInit, OnDestroy {
private onDestroy$ = new Subject<boolean>();
constructor(@Inject(SEARCH_QUERY_SERVICE_TOKEN) private searchFilterQueryBuilder: SearchHeaderQueryBuilderService,
private translationService: TranslationService,
private focusTrapFactory: ConfigurableFocusTrapFactory) {
}
constructor(
@Inject(SEARCH_QUERY_SERVICE_TOKEN) private searchFilterQueryBuilder: SearchHeaderQueryBuilderService,
private translationService: TranslationService,
private focusTrapFactory: ConfigurableFocusTrapFactory
) {}
ngOnInit() {
this.category = this.searchFilterQueryBuilder.getCategoryForColumn(this.col.key);
this.initialValue = this.value && this.value[this.col.key] ? this.value[this.col.key] : undefined;
this.initialValue = this.value?.[this.col.key] ? this.value[this.col.key] : undefined;
}
ngOnDestroy() {

View File

@@ -30,7 +30,6 @@ import { SearchFacetFiltersService } from '../../services/search-facet-filters.s
host: { class: 'adf-search-filter' }
})
export class SearchFilterComponent {
/** Toggles whether to show or not the context facet filters. */
@Input()
showContextFacets: boolean = true;
@@ -41,16 +40,18 @@ export class SearchFilterComponent {
};
displayResetButton: boolean;
constructor(@Inject(SEARCH_QUERY_SERVICE_TOKEN) public queryBuilder: SearchQueryBuilderService,
public facetFiltersService: SearchFacetFiltersService) {
if (queryBuilder.config && queryBuilder.config.facetQueries) {
constructor(
@Inject(SEARCH_QUERY_SERVICE_TOKEN) public queryBuilder: SearchQueryBuilderService,
public facetFiltersService: SearchFacetFiltersService
) {
if (queryBuilder.config?.facetQueries) {
this.facetQueriesLabel = queryBuilder.config.facetQueries.label || 'Facet Queries';
this.facetExpanded['query'] = queryBuilder.config.facetQueries.expanded;
}
if (queryBuilder.config && queryBuilder.config.facetFields) {
if (queryBuilder.config?.facetFields) {
this.facetExpanded['field'] = queryBuilder.config.facetFields.expanded;
}
if (queryBuilder.config && queryBuilder.config.facetIntervals) {
if (queryBuilder.config?.facetIntervals) {
this.facetExpanded['interval'] = queryBuilder.config.facetIntervals.expanded;
}
this.displayResetButton = this.queryBuilder.config && !!this.queryBuilder.config.resetButton;

View File

@@ -38,7 +38,6 @@ import { Observable } from 'rxjs';
template: '<div #content></div>'
})
export class SearchWidgetContainerComponent implements OnInit, OnDestroy, OnChanges {
@ViewChild('content', { read: ViewContainerRef, static: true })
content: ViewContainerRef;
@@ -62,8 +61,8 @@ export class SearchWidgetContainerComponent implements OnInit, OnDestroy, OnChan
constructor(
private searchFilterService: SearchFilterService,
@Inject(SEARCH_QUERY_SERVICE_TOKEN) private queryBuilder: BaseQueryBuilderService,
private componentFactoryResolver: ComponentFactoryResolver) {
}
private componentFactoryResolver: ComponentFactoryResolver
) {}
ngOnInit() {
const componentType = this.searchFilterService.widgets[this.selector];
@@ -85,9 +84,9 @@ export class SearchWidgetContainerComponent implements OnInit, OnDestroy, OnChan
}
private setupWidget(ref: ComponentRef<any>) {
if (ref && ref.instance) {
if (ref?.instance) {
ref.instance.id = this.id;
ref.instance.settings = {...this.settings};
ref.instance.settings = { ...this.settings };
ref.instance.context = this.queryBuilder;
if (this.value) {
ref.instance.isActive = true;
@@ -128,7 +127,7 @@ export class SearchWidgetContainerComponent implements OnInit, OnDestroy, OnChan
}
resetInnerWidget() {
if (this.componentRef && this.componentRef.instance) {
if (this.componentRef?.instance) {
this.componentRef.instance.reset();
}
}

View File

@@ -28,7 +28,8 @@ import {
TemplateRef,
ViewChild,
ViewEncapsulation,
OnDestroy
OnDestroy,
SimpleChanges
} from '@angular/core';
import { NodePaging, ResultSetPaging } from '@alfresco/js-api';
import { Subject } from 'rxjs';
@@ -45,7 +46,6 @@ import { SearchComponentInterface } from '@alfresco/adf-core';
host: { class: 'adf-search' }
})
export class SearchComponent implements SearchComponentInterface, AfterContentInit, OnChanges, OnDestroy {
@ViewChild('panel', { static: true })
panel: ElementRef;
@@ -74,8 +74,8 @@ export class SearchComponent implements SearchComponentInterface, AfterContentIn
// eslint-disable-next-line @angular-eslint/no-input-rename
@Input('class')
set classList(classList: string) {
if (classList && classList.length) {
classList.split(' ').forEach((className) => this._classList[className.trim()] = true);
if (classList?.length) {
classList.split(' ').forEach((className) => (this._classList[className.trim()] = true));
this._elementRef.nativeElement.className = '';
}
}
@@ -104,31 +104,23 @@ export class SearchComponent implements SearchComponentInterface, AfterContentIn
_classList: { [key: string]: boolean } = {};
private onDestroy$ = new Subject<boolean>();
constructor(private searchService: SearchService,
private _elementRef: ElementRef) {
this.keyPressedStream
.pipe(
debounceTime(200),
takeUntil(this.onDestroy$)
)
.subscribe(searchedWord => {
this.loadSearchResults(searchedWord);
});
constructor(private searchService: SearchService, private _elementRef: ElementRef) {
this.keyPressedStream.pipe(debounceTime(200), takeUntil(this.onDestroy$)).subscribe((searchedWord) => {
this.loadSearchResults(searchedWord);
});
searchService.dataLoaded
.pipe(takeUntil(this.onDestroy$))
.subscribe(
nodePaging => this.onSearchDataLoaded(nodePaging),
error => this.onSearchDataError(error)
);
searchService.dataLoaded.pipe(takeUntil(this.onDestroy$)).subscribe(
(nodePaging) => this.onSearchDataLoaded(nodePaging),
(error) => this.onSearchDataError(error)
);
}
ngAfterContentInit() {
this.setVisibility();
}
ngOnChanges(changes) {
if (changes.searchTerm && changes.searchTerm.currentValue) {
ngOnChanges(changes: SimpleChanges) {
if (changes.searchTerm?.currentValue) {
this.loadSearchResults(changes.searchTerm.currentValue);
}
}
@@ -174,8 +166,8 @@ export class SearchComponent implements SearchComponentInterface, AfterContentIn
}
}
onSearchDataError(error) {
if (error && error.status !== 400) {
onSearchDataError(error: { status: number }) {
if (error?.status !== 400) {
this.results = null;
this.error.emit(error);
}

View File

@@ -19,10 +19,8 @@ import { ErrorStateMatcher } from '@angular/material/core';
import { UntypedFormControl, FormGroupDirective, NgForm } from '@angular/forms';
export class LiveErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const isSubmitted = form && form.submitted;
return !!(control && control.invalid && (control.dirty || control.touched || (!control.pristine && isSubmitted)));
const isSubmitted = form?.submitted;
return !!(control?.invalid && (control.dirty || control.touched || (!control.pristine && isSubmitted)));
}
}

View File

@@ -90,7 +90,7 @@ export abstract class BaseQueryBuilderService {
// TODO: to be supported in future iterations
ranges: { [id: string]: SearchRange } = {};
constructor(protected appConfig: AppConfigService, protected alfrescoApiService: AlfrescoApiService) {
protected constructor(protected appConfig: AppConfigService, protected alfrescoApiService: AlfrescoApiService) {
this.resetToDefaults();
}
@@ -374,7 +374,7 @@ export abstract class BaseQueryBuilderService {
* @returns The primary sorting definition
*/
getPrimarySorting(): SearchSortingDefinition {
if (this.sorting && this.sorting.length > 0) {
if (this.sorting?.length > 0) {
return this.sorting[0];
}
return null;
@@ -386,10 +386,7 @@ export abstract class BaseQueryBuilderService {
* @returns Pre-configured sorting options
*/
getSortingOptions(): SearchSortingDefinition[] {
if (this.config && this.config.sorting) {
return this.config.sorting.options || [];
}
return [];
return this.config?.sorting?.options || [];
}
/**
@@ -398,7 +395,7 @@ export abstract class BaseQueryBuilderService {
* @param query Target query
* @returns Query group
*/
getQueryGroup(query) {
getQueryGroup(query: FacetQuery): string {
return query.group || this.config.facetQueries.label || 'Facet Queries';
}
@@ -408,10 +405,7 @@ export abstract class BaseQueryBuilderService {
* @returns True if defined, false otherwise
*/
get hasFacetQueries(): boolean {
if (this.config && this.config.facetQueries && this.config.facetQueries.queries && this.config.facetQueries.queries.length > 0) {
return true;
}
return false;
return this.config?.facetQueries?.queries?.length > 0;
}
/**
@@ -420,11 +414,11 @@ export abstract class BaseQueryBuilderService {
* @returns True if defined, false otherwise
*/
get hasFacetIntervals(): boolean {
return this.config && this.config.facetIntervals && this.config.facetIntervals.intervals && this.config.facetIntervals.intervals.length > 0;
return this.config?.facetIntervals?.intervals?.length > 0;
}
get hasFacetHighlight(): boolean {
return !!(this.config && this.config.highlight);
return !!this.config?.highlight;
}
protected get sort(): RequestSortDefinitionInner[] {
@@ -515,9 +509,9 @@ export abstract class BaseQueryBuilderService {
}
protected get facetFields(): RequestFacetFields {
const facetFields = this.config.facetFields && this.config.facetFields.fields;
const facetFields = this.config.facetFields?.fields;
if (facetFields && facetFields.length > 0) {
if (facetFields?.length > 0) {
return {
facets: facetFields.map(
(facet) =>

View File

@@ -40,7 +40,6 @@ const DEFAULT_PAGE_SIZE: number = 5;
providedIn: 'root'
})
export class SearchFacetFiltersService implements OnDestroy {
/** All facet field items to be displayed in the component. These are updated according to the response.
* When a new search is performed, the already existing items are updated with the new bucket count values and
* the newly received items are added to the responseFacets.
@@ -55,32 +54,27 @@ export class SearchFacetFiltersService implements OnDestroy {
private readonly facetQueriesPageSize = DEFAULT_PAGE_SIZE;
private readonly onDestroy$ = new Subject<boolean>();
constructor(@Inject(SEARCH_QUERY_SERVICE_TOKEN) public queryBuilder: SearchQueryBuilderService,
private searchService: SearchService,
private translationService: TranslationService,
private categoryService: CategoryService
constructor(
@Inject(SEARCH_QUERY_SERVICE_TOKEN) public queryBuilder: SearchQueryBuilderService,
private searchService: SearchService,
private translationService: TranslationService,
private categoryService: CategoryService
) {
if (queryBuilder.config && queryBuilder.config.facetQueries) {
if (queryBuilder.config?.facetQueries) {
this.facetQueriesPageSize = queryBuilder.config.facetQueries.pageSize || DEFAULT_PAGE_SIZE;
}
this.queryBuilder.configUpdated
.pipe(takeUntil(this.onDestroy$))
.subscribe(() => {
this.selectedBuckets = [];
this.responseFacets = null;
});
this.queryBuilder.configUpdated.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
this.selectedBuckets = [];
this.responseFacets = null;
});
this.queryBuilder.updated
.pipe(takeUntil(this.onDestroy$))
.subscribe((query) => this.queryBuilder.execute(query));
this.queryBuilder.updated.pipe(takeUntil(this.onDestroy$)).subscribe((query) => this.queryBuilder.execute(query));
this.queryBuilder.executed
.pipe(takeUntil(this.onDestroy$))
.subscribe((resultSetPaging: ResultSetPaging) => {
this.onDataLoaded(resultSetPaging);
this.searchService.dataLoaded.next(resultSetPaging);
});
this.queryBuilder.executed.pipe(takeUntil(this.onDestroy$)).subscribe((resultSetPaging: ResultSetPaging) => {
this.onDataLoaded(resultSetPaging);
this.searchService.dataLoaded.next(resultSetPaging);
});
}
onDataLoaded(data: any) {
@@ -104,17 +98,20 @@ export class SearchFacetFiltersService implements OnDestroy {
private parseFacetItems(context: ResultSetContext, configFacetFields: FacetField[], itemType: string) {
configFacetFields.forEach((facetField) => {
const responseField = this.findFacet(context, itemType, facetField.label);
const responseBuckets = this.getResponseBuckets(responseField, facetField)
.filter(this.getFilterByMinCount(facetField.mincount));
this.sortFacetBuckets(responseBuckets, facetField.settings?.bucketSortBy, facetField.settings?.bucketSortDirection ?? FacetBucketSortDirection.ASCENDING);
const responseBuckets = this.getResponseBuckets(responseField, facetField).filter(this.getFilterByMinCount(facetField.mincount));
this.sortFacetBuckets(
responseBuckets,
facetField.settings?.bucketSortBy,
facetField.settings?.bucketSortDirection ?? FacetBucketSortDirection.ASCENDING
);
const alreadyExistingField = this.findResponseFacet(itemType, facetField.label);
if (facetField.field === 'cm:categories'){
if (facetField.field === 'cm:categories') {
this.loadCategoryNames(responseBuckets);
}
if (alreadyExistingField) {
const alreadyExistingBuckets = alreadyExistingField.buckets && alreadyExistingField.buckets.items || [];
const alreadyExistingBuckets = alreadyExistingField.buckets?.items || [];
this.updateExistingBuckets(responseField, responseBuckets, alreadyExistingField, alreadyExistingBuckets);
} else if (responseField) {
@@ -166,18 +163,18 @@ export class SearchFacetFiltersService implements OnDestroy {
}
private parseFacetFields(context: ResultSetContext) {
const configFacetFields = this.queryBuilder.config.facetFields && this.queryBuilder.config.facetFields.fields || [];
const configFacetFields = this.queryBuilder.config.facetFields?.fields || [];
this.parseFacetItems(context, configFacetFields, 'field');
}
private parseFacetIntervals(context: ResultSetContext) {
const configFacetIntervals = this.queryBuilder.config.facetIntervals && this.queryBuilder.config.facetIntervals.intervals || [];
const configFacetIntervals = this.queryBuilder.config.facetIntervals?.intervals || [];
this.parseFacetItems(context, configFacetIntervals, 'interval');
}
private parseFacetQueries(context: ResultSetContext) {
const facetQuerySetting = this.queryBuilder.config.facetQueries?.settings || {};
const configFacetQueries = this.queryBuilder.config.facetQueries && this.queryBuilder.config.facetQueries.queries || [];
const configFacetQueries = this.queryBuilder.config.facetQueries?.queries || [];
const configGroups = configFacetQueries.reduce((acc, query) => {
const group = this.queryBuilder.getQueryGroup(query);
if (acc[group]) {
@@ -188,18 +185,21 @@ export class SearchFacetFiltersService implements OnDestroy {
return acc;
}, []);
const mincount = this.queryBuilder.config.facetQueries && this.queryBuilder.config.facetQueries.mincount;
const mincountFilter = this.getFilterByMinCount(mincount);
const minCount = this.queryBuilder.config.facetQueries?.mincount;
const minCountFilter = this.getFilterByMinCount(minCount);
Object.keys(configGroups).forEach((group) => {
const responseField = this.findFacet(context, 'query', group);
const responseBuckets = this.getResponseQueryBuckets(responseField, configGroups[group])
.filter(mincountFilter);
this.sortFacetBuckets(responseBuckets, facetQuerySetting?.bucketSortBy, facetQuerySetting.bucketSortDirection ?? FacetBucketSortDirection.ASCENDING);
const responseBuckets = this.getResponseQueryBuckets(responseField, configGroups[group]).filter(minCountFilter);
this.sortFacetBuckets(
responseBuckets,
facetQuerySetting?.bucketSortBy,
facetQuerySetting.bucketSortDirection ?? FacetBucketSortDirection.ASCENDING
);
const alreadyExistingField = this.findResponseFacet('query', group);
if (alreadyExistingField) {
const alreadyExistingBuckets = alreadyExistingField.buckets && alreadyExistingField.buckets.items || [];
const alreadyExistingBuckets = alreadyExistingField.buckets?.items || [];
this.updateExistingBuckets(responseField, responseBuckets, alreadyExistingField, alreadyExistingBuckets);
} else if (responseField) {
@@ -229,8 +229,7 @@ export class SearchFacetFiltersService implements OnDestroy {
}
private getResponseBuckets(responseField: GenericFacetResponse, configField: FacetField): FacetFieldBucket[] {
return ((responseField && responseField.buckets) || []).map((respBucket) => {
return (responseField?.buckets || []).map((respBucket) => {
respBucket['count'] = this.getCountValue(respBucket);
respBucket.filterQuery = respBucket.filterQuery || this.getCorrespondingFilterQuery(configField, respBucket.label);
return {
@@ -244,8 +243,7 @@ export class SearchFacetFiltersService implements OnDestroy {
private getResponseQueryBuckets(responseField: GenericFacetResponse, configGroup: any): FacetFieldBucket[] {
return (configGroup || []).map((query) => {
const respBucket = ((responseField && responseField.buckets) || [])
.find((bucket) => bucket.label === query.label) || {};
const respBucket = (responseField?.buckets || []).find((bucket) => bucket.label === query.label) || {};
respBucket['count'] = this.getCountValue(respBucket);
return {
@@ -261,7 +259,9 @@ export class SearchFacetFiltersService implements OnDestroy {
switch (sortBy) {
case FacetBucketSortBy.LABEL:
buckets.sort((bucket1, bucket2) =>
sortDirection === FacetBucketSortDirection.ASCENDING ? bucket1.label.localeCompare(bucket2.label) : bucket2.label.localeCompare(bucket1.label)
sortDirection === FacetBucketSortDirection.ASCENDING
? bucket1.label.localeCompare(bucket2.label)
: bucket2.label.localeCompare(bucket1.label)
);
break;
case FacetBucketSortBy.COUNT:
@@ -282,28 +282,26 @@ export class SearchFacetFiltersService implements OnDestroy {
return bucket.count === null ? '' : `(${bucket.count})`;
}
private getFilterByMinCount(mincountInput: number) {
return (bucket) => {
let mincount = mincountInput;
if (mincount === undefined) {
mincount = 1;
private getFilterByMinCount =
(minCountInput: number) =>
(bucket: FacetFieldBucket): boolean => {
let minCount = minCountInput;
if (minCount === undefined) {
minCount = 1;
}
return bucket.count >= mincount;
return bucket.count >= minCount;
};
}
private getCorrespondingFilterQuery(configFacetItem: FacetField, bucketLabel: string): string {
let filterQuery = null;
if (configFacetItem.field && bucketLabel) {
if (configFacetItem.sets) {
const configSet = configFacetItem.sets.find((set) => bucketLabel === set.label);
if (configSet) {
filterQuery = this.buildIntervalQuery(configFacetItem.field, configSet);
}
} else {
filterQuery = `${configFacetItem.field}:"${bucketLabel}"`;
}
@@ -315,8 +313,8 @@ export class SearchFacetFiltersService implements OnDestroy {
private buildIntervalQuery(fieldName: string, interval: any): string {
const start = interval.start;
const end = interval.end;
const startLimit = (interval.startInclusive === undefined || interval.startInclusive === true) ? '[' : '<';
const endLimit = (interval.endInclusive === undefined || interval.endInclusive === true) ? ']' : '>';
const startLimit = interval.startInclusive === undefined || interval.startInclusive === true ? '[' : '<';
const endLimit = interval.endInclusive === undefined || interval.endInclusive === true ? ']' : '>';
return `${fieldName}:${startLimit}"${start}" TO "${end}"${endLimit}`;
}
@@ -329,22 +327,27 @@ export class SearchFacetFiltersService implements OnDestroy {
return (this.responseFacets || []).find((response) => response.type === itemType && response.label === fieldLabel);
}
private updateExistingBuckets(responseField, responseBuckets, alreadyExistingField, alreadyExistingBuckets) {
private updateExistingBuckets(
responseField: GenericFacetResponse,
responseBuckets: FacetFieldBucket[],
alreadyExistingField: FacetField,
alreadyExistingBuckets: FacetFieldBucket[]
) {
const bucketsToDelete = [];
alreadyExistingBuckets
.map((bucket) => {
const responseBucket = ((responseField && responseField.buckets) || []).find((respBucket) => respBucket.label === bucket.label);
alreadyExistingBuckets.forEach((bucket) => {
const responseBucket = (responseField?.buckets || []).find((respBucket) => respBucket.label === bucket.label);
if (!responseBucket) {
bucketsToDelete.push(bucket);
}
bucket.count = this.getCountValue(responseBucket);
return bucket;
});
if (!responseBucket) {
bucketsToDelete.push(bucket);
}
bucket.count = this.getCountValue(responseBucket);
return bucket;
});
const hasSelection = this.selectedBuckets
.find((selBuckets) => alreadyExistingField.label === selBuckets.field.label && alreadyExistingField.type === selBuckets.field.type);
const hasSelection = !!this.selectedBuckets.find(
(selBuckets) => alreadyExistingField.label === selBuckets.field.label && alreadyExistingField.type === selBuckets.field.type
);
if (!hasSelection && bucketsToDelete.length) {
bucketsToDelete.forEach((bucket) => {
@@ -361,8 +364,9 @@ export class SearchFacetFiltersService implements OnDestroy {
});
}
private getBucketFilterFunction(bucketList) {
return (bucket: FacetFieldBucket): boolean => {
private getBucketFilterFunction =
(bucketList: SearchFilterList<FacetFieldBucket>) =>
(bucket: FacetFieldBucket): boolean => {
if (bucket && bucketList.filterText) {
const pattern = (bucketList.filterText || '').toLowerCase();
const label = (this.translationService.instant(bucket.display) || this.translationService.instant(bucket.label)).toLowerCase();
@@ -370,21 +374,19 @@ export class SearchFacetFiltersService implements OnDestroy {
}
return true;
};
}
private loadCategoryNames(bucketList: FacetFieldBucket[]) {
bucketList.forEach((item) => {
const categoryId = item.label.split('/').pop();
this.categoryService.getCategory(categoryId, {include: ['path']})
.pipe(catchError(error => throwError(error)))
.subscribe(
category => {
const nextAfterGeneralPathPartIndex = 3;
const pathSeparator = '/';
const path = category.entry.path.split(pathSeparator).slice(nextAfterGeneralPathPartIndex).join('/');
item.display = path ? `${path}/${category.entry.name}` : category.entry.name;
}
);
this.categoryService
.getCategory(categoryId, { include: ['path'] })
.pipe(catchError((error) => throwError(error)))
.subscribe((category) => {
const nextAfterGeneralPathPartIndex = 3;
const pathSeparator = '/';
const path = category.entry.path.split(pathSeparator).slice(nextAfterGeneralPathPartIndex).join('/');
item.display = path ? `${path}/${category.entry.name}` : category.entry.name;
});
});
}
@@ -401,14 +403,15 @@ export class SearchFacetFiltersService implements OnDestroy {
updateSelectedBuckets() {
if (this.responseFacets) {
this.selectedBuckets = [];
let facetFields = this.tabbedFacet === null ? [] : Object.keys(this.tabbedFacet?.fields).map(field => this.tabbedFacet.facets[field]);
let facetFields = this.tabbedFacet === null ? [] : Object.keys(this.tabbedFacet?.fields).map((field) => this.tabbedFacet.facets[field]);
facetFields = [...facetFields, ...this.responseFacets];
for (const facetField of facetFields) {
if (facetField?.buckets) {
this.selectedBuckets.push(
...this.queryBuilder.getUserFacetBuckets(facetField.field)
...this.queryBuilder
.getUserFacetBuckets(facetField.field)
.filter((bucket) => bucket.checked)
.map((bucket) => ({field: facetField, bucket}))
.map((bucket) => ({ field: facetField, bucket }))
);
}
}

View File

@@ -19,8 +19,8 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } fro
import { LogService, InfiniteSelectScrollDirective, AuthenticationService } from '@alfresco/adf-core';
import { SitePaging, SiteEntry, Site } from '@alfresco/js-api';
import { MatSelectChange } from '@angular/material/select';
import {LiveAnnouncer} from '@angular/cdk/a11y';
import {TranslateService} from '@ngx-translate/core';
import { LiveAnnouncer } from '@angular/cdk/a11y';
import { TranslateService } from '@ngx-translate/core';
import { SitesService } from '../common/services/sites.service';
/* eslint-disable no-shadow */
@@ -39,7 +39,6 @@ export enum Relations {
host: { class: 'adf-sites-dropdown' }
})
export class DropdownSitesComponent implements OnInit {
/** Hide the "My Files" option. */
@Input()
hideMyFiles: boolean = false;
@@ -81,12 +80,13 @@ export class DropdownSitesComponent implements OnInit {
selected: SiteEntry = null;
MY_FILES_VALUE = '-my-';
constructor(private authService: AuthenticationService,
private sitesService: SitesService,
private logService: LogService,
private liveAnnouncer: LiveAnnouncer,
private translateService: TranslateService) {
}
constructor(
private authService: AuthenticationService,
private sitesService: SitesService,
private logService: LogService,
private liveAnnouncer: LiveAnnouncer,
private translateService: TranslateService
) {}
ngOnInit() {
if (!this.siteList) {
@@ -102,10 +102,12 @@ export class DropdownSitesComponent implements OnInit {
}
selectedSite(event: MatSelectChange) {
this.liveAnnouncer.announce(this.translateService.instant('ADF_DROPDOWN.SELECTION_ARIA_LABEL', {
placeholder: this.translateService.instant(this.placeholder),
selectedOption: this.translateService.instant(event.value.entry.title)
}));
this.liveAnnouncer.announce(
this.translateService.instant('ADF_DROPDOWN.SELECTION_ARIA_LABEL', {
placeholder: this.translateService.instant(this.placeholder),
selectedOption: this.translateService.instant(event.value.entry.title)
})
);
this.change.emit(event.value);
}
@@ -121,8 +123,8 @@ export class DropdownSitesComponent implements OnInit {
extendedOptions.relations = [this.relations];
}
this.sitesService.getSites(extendedOptions).subscribe((sitePaging: SitePaging) => {
this.sitesService.getSites(extendedOptions).subscribe(
(sitePaging: SitePaging) => {
if (!this.siteList) {
this.siteList = this.relations === Relations.Members ? this.filteredResultsByMember(sitePaging) : sitePaging;
@@ -137,7 +139,6 @@ export class DropdownSitesComponent implements OnInit {
this.value = this.MY_FILES_VALUE;
}
}
} else {
const siteList: SitePaging = this.relations === Relations.Members ? this.filteredResultsByMember(sitePaging) : sitePaging;
@@ -155,7 +156,8 @@ export class DropdownSitesComponent implements OnInit {
},
(error) => {
this.logService.error(error);
});
}
);
}
showLoading(): boolean {
@@ -167,7 +169,7 @@ export class DropdownSitesComponent implements OnInit {
}
private siteListHasMoreItems(): boolean {
return this.siteList && this.siteList.list.pagination && this.siteList.list.pagination.hasMoreItems;
return this.siteList?.list.pagination?.hasMoreItems;
}
private filteredResultsByMember(sites: SitePaging): SitePaging {
@@ -177,7 +179,9 @@ export class DropdownSitesComponent implements OnInit {
}
private isCurrentUserMember(site: SiteEntry, loggedUserName: string): boolean {
return site.entry.visibility === 'PUBLIC' ||
!!site.relations.members.list.entries.find((member) => member.entry.id.toLowerCase() === loggedUserName.toLowerCase());
return (
site.entry.visibility === 'PUBLIC' ||
!!site.relations.members.list.entries.find((member) => member.entry.id.toLowerCase() === loggedUserName.toLowerCase())
);
}
}

View File

@@ -27,9 +27,7 @@ import { NodeEntry } from '@alfresco/js-api';
templateUrl: './tree-view.component.html',
styleUrls: ['./tree-view.component.scss']
})
export class TreeViewComponent implements OnChanges {
/** Identifier of the node to display. */
@Input()
nodeId: string;
@@ -51,8 +49,7 @@ export class TreeViewComponent implements OnChanges {
}
ngOnChanges(changes: SimpleChanges) {
if (changes['nodeId'] && changes['nodeId'].currentValue &&
changes['nodeId'].currentValue !== changes['nodeId'].previousValue) {
if (changes['nodeId']?.currentValue && changes['nodeId'].currentValue !== changes['nodeId'].previousValue) {
this.loadTreeNode();
} else {
this.dataSource.data = [];
@@ -70,12 +67,11 @@ export class TreeViewComponent implements OnChanges {
hasChild = (_: number, nodeData: TreeBaseNode) => nodeData.expandable;
private loadTreeNode() {
this.treeViewService.getTreeNodes(this.nodeId)
.subscribe(
(treeNode: TreeBaseNode[]) => {
this.dataSource.data = treeNode;
},
(error) => this.error.emit(error)
);
this.treeViewService.getTreeNodes(this.nodeId).subscribe(
(treeNode: TreeBaseNode[]) => {
this.dataSource.data = treeNode;
},
(error) => this.error.emit(error)
);
}
}

View File

@@ -23,7 +23,7 @@ import { TreeNode } from '../models/tree-node.interface';
import { TreeResponse } from '../models/tree-response.interface';
@Injectable({ providedIn: 'root' })
export abstract class TreeService<T extends TreeNode> extends DataSource<T> {
export abstract class TreeService<T extends TreeNode> extends DataSource<T> {
public readonly treeControl: FlatTreeControl<T>;
public treeNodesSource = new BehaviorSubject<T[]>([]);
@@ -38,7 +38,10 @@ export abstract class TreeService<T extends TreeNode> extends DataSource<T> {
constructor() {
super();
this.treeControl = new FlatTreeControl<T>(node => node.level, node => node.hasChildren);
this.treeControl = new FlatTreeControl<T>(
(node) => node.level,
(node) => node.hasChildren
);
this.treeNodes = [];
}
@@ -66,7 +69,7 @@ export abstract class TreeService<T extends TreeNode> extends DataSource<T> {
* @param nodeToCollapse Node to be collapsed
*/
public collapseNode(nodeToCollapse: T): void {
if (nodeToCollapse != null && nodeToCollapse.hasChildren) {
if (nodeToCollapse?.hasChildren) {
this.treeControl.collapse(nodeToCollapse);
const children: T[] = this.treeNodes.filter((node: T) => nodeToCollapse.id === node.parentId);
children.forEach((child: T) => {
@@ -142,9 +145,7 @@ export abstract class TreeService<T extends TreeNode> extends DataSource<T> {
const index: number = this.treeNodes.indexOf(nodeToCollapse);
this.treeNodes.splice(index, 1);
if (nodeToCollapse.hasChildren) {
this.treeNodes
.filter((node: T) => nodeToCollapse.id === node.parentId)
.forEach((child: T) => this.collapseInnerNode(child));
this.treeNodes.filter((node: T) => nodeToCollapse.id === node.parentId).forEach((child: T) => this.collapseInnerNode(child));
}
}
}

View File

@@ -36,9 +36,11 @@ export class FileUploadingListRowComponent {
}
showCancelledStatus(): boolean {
return this.file.status === FileUploadStatus.Cancelled ||
return (
this.file.status === FileUploadStatus.Cancelled ||
this.file.status === FileUploadStatus.Aborted ||
this.file.status === FileUploadStatus.Deleted;
this.file.status === FileUploadStatus.Deleted
);
}
get versionNumber(): string {
@@ -46,29 +48,19 @@ export class FileUploadingListRowComponent {
}
get mimeType(): string {
if (this.file && this.file.file && this.file.file.type) {
return this.file.file.type;
}
return 'default';
return this.file?.file?.type || 'default';
}
isUploadVersion(): boolean {
return (
!!this.file.data &&
this.file.options &&
this.file.options.newVersion &&
this.file.data.entry.properties &&
this.file.data.entry.properties['cm:versionLabel']
);
return !!this.file.data && this.file.options?.newVersion && this.file.data.entry.properties?.['cm:versionLabel'];
}
canCancelUpload(): boolean {
return this.file && this.file.status === FileUploadStatus.Pending;
return this.file?.status === FileUploadStatus.Pending;
}
isUploadError(): boolean {
return this.file && this.file.status === FileUploadStatus.Error;
return this.file?.status === FileUploadStatus.Error;
}
isUploading(): boolean {
@@ -76,10 +68,10 @@ export class FileUploadingListRowComponent {
}
isUploadComplete(): boolean {
return this.file.status === FileUploadStatus.Complete && !this.isUploadVersion();
return this.file?.status === FileUploadStatus.Complete && !this.isUploadVersion();
}
isUploadVersionComplete(): boolean {
return this.file && (this.file.status === FileUploadStatus.Complete && this.isUploadVersion());
return this.file?.status === FileUploadStatus.Complete && this.isUploadVersion();
}
}

View File

@@ -16,10 +16,7 @@
*/
import { EXTENDIBLE_COMPONENT, FileUtils, LogService } from '@alfresco/adf-core';
import {
Component, EventEmitter, forwardRef, Input,
OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation, inject
} from '@angular/core';
import { Component, EventEmitter, forwardRef, Input, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation, inject } from '@angular/core';
import { NodesApiService } from '../../common/services/nodes-api.service';
import { ContentService } from '../../common/services/content.service';
import { AllowableOperationsEnum } from '../../common/models/allowable-operations.enum';
@@ -33,9 +30,7 @@ import { NodeAllowableOperationSubject } from '../../interfaces/node-allowable-o
selector: 'adf-upload-button',
templateUrl: './upload-button.component.html',
styleUrls: ['./upload-button.component.scss'],
viewProviders: [
{ provide: EXTENDIBLE_COMPONENT, useExisting: forwardRef(() => UploadButtonComponent) }
],
viewProviders: [{ provide: EXTENDIBLE_COMPONENT, useExisting: forwardRef(() => UploadButtonComponent) }],
encapsulation: ViewEncapsulation.None
})
export class UploadButtonComponent extends UploadBase implements OnInit, OnChanges, NodeAllowableOperationSubject {
@@ -78,7 +73,7 @@ export class UploadButtonComponent extends UploadBase implements OnInit, OnChang
ngOnChanges(changes: SimpleChanges) {
const rootFolderId = changes['rootFolderId'];
if (rootFolderId && rootFolderId.currentValue) {
if (rootFolderId?.currentValue) {
this.checkPermission();
}
}
@@ -88,7 +83,7 @@ export class UploadButtonComponent extends UploadBase implements OnInit, OnChang
}
onFilesAdded($event: any): void {
const files: File[] = FileUtils.toFileArray($event.currentTarget.files);
const files = FileUtils.toFileArray($event.currentTarget.files);
if (this.hasAllowableOperations) {
this.uploadFiles(files);
@@ -101,7 +96,7 @@ export class UploadButtonComponent extends UploadBase implements OnInit, OnChang
onClickUploadButton(): void {
if (this.file) {
const files: File[] = [this.file];
const files = [this.file];
if (this.hasAllowableOperations) {
this.uploadFiles(files);
@@ -113,7 +108,7 @@ export class UploadButtonComponent extends UploadBase implements OnInit, OnChang
onDirectoryAdded($event: any): void {
if (this.hasAllowableOperations) {
const files: File[] = FileUtils.toFileArray($event.currentTarget.files);
const files = FileUtils.toFileArray($event.currentTarget.files);
this.uploadFiles(files);
} else {
this.permissionEvent.emit(new PermissionModel({ type: 'content', action: 'upload', permission: 'create' }));
@@ -132,10 +127,10 @@ export class UploadButtonComponent extends UploadBase implements OnInit, OnChang
this.nodesApiService.getNode(this.rootFolderId, opts).subscribe(
(res) => this.permissionValue.next(this.nodeHasPermission(res, AllowableOperationsEnum.CREATE)),
(error: { error: Error }) => {
if (error && error.error) {
if (error?.error) {
this.error.emit({ error: error.error.message } as any);
} else {
this.error.emit({ error: 'FILE_UPLOAD.BUTTON.PERMISSION_CHECK_ERROR'} as any);
this.error.emit({ error: 'FILE_UPLOAD.BUTTON.PERMISSION_CHECK_ERROR' } as any);
}
}
);

View File

@@ -22,15 +22,14 @@ import { UploadBase } from './base-upload/upload-base';
import { AllowableOperationsEnum } from '../../common/models/allowable-operations.enum';
import { ContentService } from '../../common/services/content.service';
import { FileModel } from '../../common/models/file.model';
import { Node } from '@alfresco/js-api';
@Component({
selector: 'adf-upload-drag-area',
templateUrl: './upload-drag-area.component.html',
styleUrls: ['./upload-drag-area.component.scss'],
host: { class: 'adf-upload-drag-area' },
viewProviders: [
{provide: EXTENDIBLE_COMPONENT, useExisting: forwardRef(() => UploadDragAreaComponent)}
],
viewProviders: [{ provide: EXTENDIBLE_COMPONENT, useExisting: forwardRef(() => UploadDragAreaComponent) }],
encapsulation: ViewEncapsulation.None
})
export class UploadDragAreaComponent extends UploadBase implements NodeAllowableOperationSubject {
@@ -70,9 +69,12 @@ export class UploadDragAreaComponent extends UploadBase implements NodeAllowable
const messageTranslate = this.translationService.instant('FILE_UPLOAD.MESSAGES.PROGRESS');
const actionTranslate = this.translationService.instant('FILE_UPLOAD.ACTION.UNDO');
this.notificationService.openSnackMessageAction(messageTranslate, actionTranslate).onAction().subscribe(() => {
this.uploadService.cancelUpload(...latestFilesAdded);
});
this.notificationService
.openSnackMessageAction(messageTranslate, actionTranslate)
.onAction()
.subscribe(() => {
this.uploadService.cancelUpload(...latestFilesAdded);
});
}
/** Returns true or false considering the component options and node permissions */
@@ -88,27 +90,29 @@ export class UploadDragAreaComponent extends UploadBase implements NodeAllowable
onUploadFiles(event: CustomEvent) {
event.stopPropagation();
event.preventDefault();
const isAllowed: boolean = this.isTargetNodeFolder(event) ?
this.contentService.hasAllowableOperations(event.detail.data.obj.entry, AllowableOperationsEnum.CREATE)
: this.contentService.hasAllowableOperations(event.detail.data.obj.entry, AllowableOperationsEnum.UPDATE);
const node: Node = event.detail.data.obj.entry;
const files: FileInfo[] = event.detail?.files || [];
const isAllowed: boolean = this.isTargetNodeFolder(node)
? this.contentService.hasAllowableOperations(node, AllowableOperationsEnum.CREATE)
: this.contentService.hasAllowableOperations(node, AllowableOperationsEnum.UPDATE);
if (isAllowed) {
if (!this.isTargetNodeFolder(event) && event.detail.files.length === 1) {
if (!this.isTargetNodeFolder(node) && files.length === 1) {
this.updateFileVersion.emit(event);
} else {
const fileInfo: FileInfo[] = event.detail.files;
if (this.isTargetNodeFolder(event)) {
const destinationFolderName = event.detail.data.obj.entry.name;
fileInfo.map((file) => file.relativeFolder = destinationFolderName ? destinationFolderName.concat(file.relativeFolder) : file.relativeFolder);
if (this.isTargetNodeFolder(node)) {
files.forEach((file) => (file.relativeFolder = node.name ? node.name.concat(file.relativeFolder) : file.relativeFolder));
}
if (fileInfo && fileInfo.length > 0) {
this.uploadFilesInfo(fileInfo);
if (files?.length > 0) {
this.uploadFilesInfo(files);
}
}
}
}
private isTargetNodeFolder(event: CustomEvent): boolean {
return event.detail.data.obj && event.detail.data.obj.entry.isFolder;
private isTargetNodeFolder(node: Node): boolean {
return !!node?.isFolder;
}
}

View File

@@ -42,17 +42,7 @@ import {
ViewUtilService
} from '@alfresco/adf-core';
import { Subject } from 'rxjs';
import {
ContentApi,
Node,
NodeEntry,
NodesApi,
RenditionEntry,
SharedlinksApi,
Version,
VersionEntry,
VersionsApi
} from '@alfresco/js-api';
import { ContentApi, Node, NodeEntry, NodesApi, RenditionEntry, SharedlinksApi, Version, VersionEntry, VersionsApi } from '@alfresco/js-api';
import { RenditionService } from '../../common/services/rendition.service';
import { MatDialog } from '@angular/material/dialog';
import { filter, takeUntil } from 'rxjs/operators';
@@ -66,12 +56,11 @@ import { NodeActionsService } from '../../document-list';
selector: 'adf-alfresco-viewer',
templateUrl: './alfresco-viewer.component.html',
styleUrls: ['./alfresco-viewer.component.scss'],
host: {class: 'adf-alfresco-viewer'},
host: { class: 'adf-alfresco-viewer' },
encapsulation: ViewEncapsulation.None,
providers: [ViewUtilService]
})
export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
@ViewChild('adfViewer')
adfViewer: ViewerComponent<{ node: Node }>;
@@ -204,8 +193,8 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
tracks: Track[] = [];
readOnly: boolean = true;
sidebarRightTemplateContext: { node: Node } = {node: null};
sidebarLeftTemplateContext: { node: Node } = {node: null};
sidebarRightTemplateContext: { node: Node } = { node: null };
sidebarLeftTemplateContext: { node: Node } = { node: null };
_sharedLinksApi: SharedlinksApi;
get sharedLinksApi(): SharedlinksApi {
@@ -231,27 +220,33 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
return this._contentApi;
}
constructor(private apiService: AlfrescoApiService,
private nodesApiService: NodesApiService,
private renditionService: RenditionService,
private viewUtilService: ViewUtilService,
private logService: LogService,
private contentService: ContentService,
private uploadService: UploadService,
public dialog: MatDialog,
private cdr: ChangeDetectorRef,
private nodeActionsService: NodeActionsService) {
constructor(
private apiService: AlfrescoApiService,
private nodesApiService: NodesApiService,
private renditionService: RenditionService,
private viewUtilService: ViewUtilService,
private logService: LogService,
private contentService: ContentService,
private uploadService: UploadService,
public dialog: MatDialog,
private cdr: ChangeDetectorRef,
private nodeActionsService: NodeActionsService
) {
renditionService.maxRetries = this.maxRetries;
}
ngOnInit() {
this.nodesApiService.nodeUpdated.pipe(
filter((node) => node && node.id === this.nodeId &&
(node.name !== this.fileName ||
this.getNodeVersionProperty(this.nodeEntry.entry) !== this.getNodeVersionProperty(node))),
takeUntil(this.onDestroy$)
).subscribe((node) => this.onNodeUpdated(node));
this.nodesApiService.nodeUpdated
.pipe(
filter(
(node) =>
node &&
node.id === this.nodeId &&
(node.name !== this.fileName || this.getNodeVersionProperty(this.nodeEntry.entry) !== this.getNodeVersionProperty(node))
),
takeUntil(this.onDestroy$)
)
.subscribe((node) => this.onNodeUpdated(node));
}
private async onNodeUpdated(node: Node) {
@@ -282,7 +277,7 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
private async setupNode() {
try {
this.nodeEntry = await this.nodesApi.getNode(this.nodeId, {include: ['allowableOperations']});
this.nodeEntry = await this.nodesApi.getNode(this.nodeId, { include: ['allowableOperations'] });
if (this.versionId) {
this.versionEntry = await this.versionsApi.getVersion(this.nodeId, this.versionId);
await this.setUpNodeFile(this.nodeEntry.entry, this.versionEntry.entry);
@@ -297,24 +292,24 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
}
private async setUpNodeFile(nodeData: Node, versionData?: Version): Promise<void> {
this.readOnly = !this.contentService.hasAllowableOperations(nodeData, 'update');
let mimeType;
let urlFileContent;
let mimeType: string;
let urlFileContent: string;
if (versionData && versionData.content) {
if (versionData?.content) {
mimeType = versionData.content.mimeType;
} else if (nodeData.content) {
mimeType = nodeData.content.mimeType;
}
const currentFileVersion = this.nodeEntry?.entry?.properties && this.nodeEntry.entry.properties['cm:versionLabel'] ?
encodeURI(this.nodeEntry?.entry?.properties['cm:versionLabel']) : encodeURI('1.0');
const currentFileVersion = this.nodeEntry?.entry?.properties?.['cm:versionLabel']
? encodeURI(this.nodeEntry?.entry?.properties['cm:versionLabel'])
: encodeURI('1.0');
urlFileContent = versionData ? this.contentApi.getVersionContentUrl(this.nodeId, versionData.id) :
this.contentApi.getContentUrl(this.nodeId);
urlFileContent = this.cacheBusterNumber ? urlFileContent + '&' + currentFileVersion + '&' + this.cacheBusterNumber :
urlFileContent + '&' + currentFileVersion;
urlFileContent = versionData ? this.contentApi.getVersionContentUrl(this.nodeId, versionData.id) : this.contentApi.getContentUrl(this.nodeId);
urlFileContent = this.cacheBusterNumber
? urlFileContent + '&' + currentFileVersion + '&' + this.cacheBusterNumber
: urlFileContent + '&' + currentFileVersion;
const fileExtension = this.viewUtilService.getFileExtension(versionData ? versionData.name : nodeData.name);
this.fileName = versionData ? versionData.name : nodeData.name;
@@ -349,10 +344,7 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
const viewerType = this.viewUtilService.getViewerType(fileExtension, mimeType);
if (viewerType === 'unknown') {
({
url: urlFileContent,
mimeType
} = await this.getSharedLinkRendition(this.sharedLinkId));
({ url: urlFileContent, mimeType } = await this.getSharedLinkRendition(this.sharedLinkId));
}
this.mimeType = mimeType;
this.urlFileContent = urlFileContent;
@@ -363,7 +355,7 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
const rendition: RenditionEntry = await this.sharedLinksApi.getSharedLinkRendition(sharedId, 'pdf');
if (rendition.entry.status.toString() === 'CREATED') {
const urlFileContent = this.contentApi.getSharedLinkRenditionUrl(sharedId, 'pdf');
return {url: urlFileContent, mimeType: 'application/pdf'};
return { url: urlFileContent, mimeType: 'application/pdf' };
}
} catch (error) {
this.logService.error(error);
@@ -371,8 +363,7 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
const rendition: RenditionEntry = await this.sharedLinksApi.getSharedLinkRendition(sharedId, 'imgpreview');
if (rendition.entry.status.toString() === 'CREATED') {
const urlFileContent = this.contentApi.getSharedLinkRenditionUrl(sharedId, 'imgpreview');
return {url: urlFileContent, mimeType: 'image/png'};
return { url: urlFileContent, mimeType: 'image/png' };
}
} catch (renditionError) {
this.logService.error(renditionError);
@@ -404,7 +395,7 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
onSubmitFile(newImageBlob: Blob) {
if (this?.nodeEntry?.entry?.id && !this.readOnly) {
const newImageFile: File = new File([newImageBlob], this?.nodeEntry?.entry?.name, {type: this?.nodeEntry?.entry?.content?.mimeType});
const newImageFile: File = new File([newImageBlob], this?.nodeEntry?.entry?.name, { type: this?.nodeEntry?.entry?.content?.mimeType });
const newFile = new FileModel(
newImageFile,
{