Merge branch 'master' into VM/RED-3982
This commit is contained in:
commit
be0dbbba77
@ -95,9 +95,9 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: `:${DOSSIER_ID}/file/:${FILE_ID}`,
|
||||
canActivate: [CompositeRouteGuard, WebViewerLoadedGuard],
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [DossierFilesGuard],
|
||||
routeGuards: [DossierFilesGuard, WebViewerLoadedGuard],
|
||||
breadcrumbs: [BreadcrumbTypes.dossierTemplate, BreadcrumbTypes.dossier, BreadcrumbTypes.file],
|
||||
dossiersService: ACTIVE_DOSSIERS_SERVICE,
|
||||
},
|
||||
|
||||
@ -53,6 +53,7 @@ import { LoggerRulesService } from '@services/logger-rules.service';
|
||||
import { ILoggerConfig } from '@red/domain';
|
||||
import { SystemPreferencesService } from '@services/system-preferences.service';
|
||||
import { PdfViewerModule } from './modules/pdf-viewer/pdf-viewer.module';
|
||||
import { LicenseService } from '@services/license.service';
|
||||
|
||||
export function httpLoaderFactory(httpClient: HttpClient, configService: ConfigService): PruningTranslationLoader {
|
||||
return new PruningTranslationLoader(httpClient, '/assets/i18n/', `.json?version=${configService.values.FRONTEND_APP_VERSION}`);
|
||||
@ -181,6 +182,7 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
|
||||
LanguageService,
|
||||
UserService,
|
||||
UserPreferenceService,
|
||||
LicenseService,
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@ -11,7 +11,7 @@ import { UserListingScreenComponent } from './screens/user-listing/user-listing-
|
||||
import { DossierTemplateBreadcrumbsComponent } from './components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component';
|
||||
import { ColorPickerModule } from 'ngx-color-picker';
|
||||
import { AddEditFileAttributeDialogComponent } from './dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component';
|
||||
import { AddEditDossierTemplateDialogComponent } from './dialogs/add-edit-dossier-template-dialog/add-edit-dossier-template-dialog.component';
|
||||
import { AddEditCloneDossierTemplateDialogComponent } from './dialogs/add-edit-dossier-template-dialog/add-edit-clone-dossier-template-dialog.component';
|
||||
import { AddEntityDialogComponent } from './dialogs/add-entity-dialog/add-entity-dialog.component';
|
||||
import { EditColorDialogComponent } from './dialogs/edit-color-dialog/edit-color-dialog.component';
|
||||
import { AdminDialogService } from './services/admin-dialog.service';
|
||||
@ -41,7 +41,6 @@ import { AddEditDossierStateDialogComponent } from './dialogs/add-edit-dossier-s
|
||||
import { A11yModule } from '@angular/cdk/a11y';
|
||||
import { ConfirmDeleteDossierStateDialogComponent } from './dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component';
|
||||
import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component';
|
||||
import { CloneDossierTemplateDialogComponent } from './dialogs/clone-dossier-template-dialog/clone-dossier-template-dialog.component';
|
||||
import { AdminSideNavComponent } from './admin-side-nav/admin-side-nav.component';
|
||||
import { SystemPreferencesFormComponent } from './screens/general-config/system-preferences-form/system-preferences-form.component';
|
||||
import { ConfigureCertificateDialogComponent } from './dialogs/configure-digital-signature-dialog/configure-certificate-dialog.component';
|
||||
@ -49,10 +48,9 @@ import { PkcsSignatureConfigurationComponent } from './dialogs/configure-digital
|
||||
import { KmsSignatureConfigurationComponent } from './dialogs/configure-digital-signature-dialog/form/kms-signature-configuration/kms-signature-configuration.component';
|
||||
|
||||
const dialogs = [
|
||||
AddEditDossierTemplateDialogComponent,
|
||||
AddEditCloneDossierTemplateDialogComponent,
|
||||
AddEntityDialogComponent,
|
||||
AddEditFileAttributeDialogComponent,
|
||||
CloneDossierTemplateDialogComponent,
|
||||
EditColorDialogComponent,
|
||||
SmtpAuthDialogComponent,
|
||||
AddEditUserDialogComponent,
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
<section class="dialog">
|
||||
<div
|
||||
[translateParams]="{
|
||||
type: dossierTemplate ? 'edit' : 'create',
|
||||
type: dossierTemplate ? (data.clone ? 'clone' : 'edit') : 'create',
|
||||
name: dossierTemplate?.name
|
||||
}"
|
||||
[translate]="'add-edit-dossier-template.title'"
|
||||
[translate]="'add-edit-clone-dossier-template.title'"
|
||||
class="dialog-header heading-l"
|
||||
></div>
|
||||
|
||||
<form [formGroup]="form">
|
||||
<div class="dialog-content">
|
||||
<div class="iqser-input-group required w-300">
|
||||
<label translate="add-edit-dossier-template.form.name"></label>
|
||||
<label translate="add-edit-clone-dossier-template.form.name"></label>
|
||||
<input
|
||||
[placeholder]="'add-edit-dossier-template.form.name-placeholder' | translate"
|
||||
[placeholder]="'add-edit-clone-dossier-template.form.name-placeholder' | translate"
|
||||
formControlName="name"
|
||||
name="name"
|
||||
type="text"
|
||||
@ -21,9 +21,9 @@
|
||||
</div>
|
||||
|
||||
<div class="iqser-input-group w-400">
|
||||
<label translate="add-edit-dossier-template.form.description"></label>
|
||||
<label translate="add-edit-clone-dossier-template.form.description"></label>
|
||||
<textarea
|
||||
[placeholder]="'add-edit-dossier-template.form.description-placeholder' | translate"
|
||||
[placeholder]="'add-edit-clone-dossier-template.form.description-placeholder' | translate"
|
||||
formControlName="description"
|
||||
name="description"
|
||||
rows="4"
|
||||
@ -34,11 +34,11 @@
|
||||
<div class="validity">
|
||||
<div>
|
||||
<mat-checkbox (change)="toggleHasValid('from')" [checked]="hasValidFrom" class="filter-menu-checkbox" color="primary">
|
||||
{{ 'add-edit-dossier-template.form.valid-from' | translate }}
|
||||
{{ 'add-edit-clone-dossier-template.form.valid-from' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
<mat-checkbox (change)="toggleHasValid('to')" [checked]="hasValidTo" class="filter-menu-checkbox" color="primary">
|
||||
{{ 'add-edit-dossier-template.form.valid-to' | translate }}
|
||||
{{ 'add-edit-clone-dossier-template.form.valid-to' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
@ -83,7 +83,7 @@
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button (click)="save()" [disabled]="disabled" color="primary" mat-flat-button type="button">
|
||||
{{ 'add-edit-dossier-template.save' | translate }}
|
||||
{{ 'add-edit-clone-dossier-template.save' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@ -12,11 +12,16 @@ import { DictionaryService } from '@services/entity-services/dictionary.service'
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
|
||||
interface EditCloneTemplateData {
|
||||
dossierTemplateId: string;
|
||||
clone?: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: './add-edit-dossier-template-dialog.component.html',
|
||||
styleUrls: ['./add-edit-dossier-template-dialog.component.scss'],
|
||||
templateUrl: './add-edit-clone-dossier-template-dialog.component.html',
|
||||
styleUrls: ['./add-edit-clone-dossier-template-dialog.component.scss'],
|
||||
})
|
||||
export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
|
||||
export class AddEditCloneDossierTemplateDialogComponent extends BaseDialogComponent {
|
||||
hasValidFrom: boolean;
|
||||
hasValidTo: boolean;
|
||||
downloadTypesEnum: DownloadFileType[] = ['ORIGINAL', 'PREVIEW', 'DELTA_PREVIEW', 'REDACTED'];
|
||||
@ -36,12 +41,12 @@ export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
protected readonly _injector: Injector,
|
||||
protected readonly _dialogRef: MatDialogRef<AddEditDossierTemplateDialogComponent>,
|
||||
protected readonly _dialogRef: MatDialogRef<AddEditCloneDossierTemplateDialogComponent>,
|
||||
private readonly _loadingService: LoadingService,
|
||||
@Inject(MAT_DIALOG_DATA) readonly dossierTemplateId: string,
|
||||
@Inject(MAT_DIALOG_DATA) readonly data: EditCloneTemplateData,
|
||||
) {
|
||||
super(_injector, _dialogRef, !!dossierTemplateId);
|
||||
this.dossierTemplate = this._dossierTemplatesService.find(dossierTemplateId);
|
||||
super(_injector, _dialogRef, !!data && !data.clone);
|
||||
this.dossierTemplate = this._dossierTemplatesService.find(this.data?.dossierTemplateId);
|
||||
this.form = this._getForm();
|
||||
this.initialFormValue = this.form.getRawValue();
|
||||
this.hasValidFrom = !!this.dossierTemplate?.validFrom;
|
||||
@ -81,13 +86,17 @@ export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
|
||||
validFrom: this.hasValidFrom ? this.form.get('validFrom').value : null,
|
||||
validTo: this.hasValidTo ? this.form.get('validTo').value : null,
|
||||
} as IDossierTemplate;
|
||||
await firstValueFrom(this._dossierTemplatesService.createOrUpdate(dossierTemplate));
|
||||
if (this.data?.clone) {
|
||||
await firstValueFrom(this._dossierTemplatesService.clone(this.dossierTemplate.id, dossierTemplate));
|
||||
} else {
|
||||
await firstValueFrom(this._dossierTemplatesService.createOrUpdate(dossierTemplate));
|
||||
}
|
||||
this._dialogRef.close(true);
|
||||
} catch (error: any) {
|
||||
const message =
|
||||
error.status === HttpStatusCode.Conflict
|
||||
? _('add-edit-dossier-template.error.conflict')
|
||||
: _('add-edit-dossier-template.error.generic');
|
||||
? _('add-edit-clone-dossier-template.error.conflict')
|
||||
: _('add-edit-clone-dossier-template.error.generic');
|
||||
this._toaster.error(message, { error });
|
||||
}
|
||||
this._loadingService.stop();
|
||||
@ -95,7 +104,7 @@ export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
|
||||
|
||||
private _getForm(): FormGroup {
|
||||
return this._formBuilder.group({
|
||||
name: [this.dossierTemplate?.name, Validators.required],
|
||||
name: [this._getCloneName(), Validators.required],
|
||||
description: [this.dossierTemplate?.description],
|
||||
validFrom: [
|
||||
this.dossierTemplate?.validFrom ? dayjs(this.dossierTemplate?.validFrom) : null,
|
||||
@ -109,6 +118,32 @@ export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
|
||||
});
|
||||
}
|
||||
|
||||
private _getCloneName(): string {
|
||||
if (this.data?.clone) {
|
||||
const templateName = this.dossierTemplate.name.trim();
|
||||
|
||||
let nameOfClonedTemplate: string = templateName.split('Copy of ').filter(n => n)[0];
|
||||
nameOfClonedTemplate = nameOfClonedTemplate.split(/\(\s*\d+\s*\)$/)[0].trim();
|
||||
|
||||
const allTemplatesNames = this._dossierTemplatesService.all.map(t => t.name);
|
||||
|
||||
let clonesCount = 0;
|
||||
for (const name of allTemplatesNames) {
|
||||
const splitName = name.split(nameOfClonedTemplate);
|
||||
const suffixRegExp = new RegExp(/^\(\s*\d+\s*\)$/);
|
||||
if (splitName[0] === 'Copy of ' && (splitName[1].trim().match(suffixRegExp) || splitName[1] === '')) {
|
||||
clonesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (clonesCount >= 1) {
|
||||
return `Copy of ${nameOfClonedTemplate} ${clonesCount === 1 ? '(1)' : `(${clonesCount})`}`;
|
||||
}
|
||||
return `Copy of ${nameOfClonedTemplate}`;
|
||||
}
|
||||
return this.dossierTemplate?.name;
|
||||
}
|
||||
|
||||
private _applyValidityIntervalConstraints(value): boolean {
|
||||
if (applyIntervalConstraints(value, this._previousValidFrom, this._previousValidTo, this.form, 'validFrom', 'validTo')) {
|
||||
return true;
|
||||
@ -130,4 +165,11 @@ export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
get disabled(): boolean {
|
||||
if (!this.data?.clone) {
|
||||
return super.disabled;
|
||||
}
|
||||
return !this.valid;
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
<section class="dialog">
|
||||
<div translate="clone-dossier-template.title" class="dialog-header heading-l"></div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<div class="iqser-input-group required w-full">
|
||||
<label translate="clone-dossier-template.content.name"></label>
|
||||
<input
|
||||
[placeholder]="'clone-dossier-template.content.name-placeholder' | translate"
|
||||
[(ngModel)]="nameOfClonedDossierTemplate"
|
||||
name="name"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button color="primary" mat-flat-button type="submit" (click)="save()" [disabled]="!nameOfClonedDossierTemplate">
|
||||
{{ 'clone-dossier-template.actions.save' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
||||
</section>
|
||||
@ -1,66 +0,0 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { DossierTemplate } from '@red/domain';
|
||||
import { LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
@Component({
|
||||
templateUrl: './clone-dossier-template-dialog.component.html',
|
||||
styleUrls: ['./clone-dossier-template-dialog.component.scss'],
|
||||
})
|
||||
export class CloneDossierTemplateDialogComponent {
|
||||
nameOfClonedDossierTemplate: string;
|
||||
private readonly _dossierTemplate: DossierTemplate;
|
||||
|
||||
constructor(
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||
protected readonly _dialogRef: MatDialogRef<CloneDossierTemplateDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) readonly dossierTemplateId: string,
|
||||
) {
|
||||
this._dossierTemplate = this._dossierTemplatesService.find(dossierTemplateId);
|
||||
this.nameOfClonedDossierTemplate = this._getCloneName();
|
||||
}
|
||||
|
||||
async save() {
|
||||
this._loadingService.start();
|
||||
try {
|
||||
await firstValueFrom(
|
||||
this._dossierTemplatesService.clone(this.dossierTemplateId, {
|
||||
...this._dossierTemplate,
|
||||
name: this.nameOfClonedDossierTemplate,
|
||||
}),
|
||||
);
|
||||
this._dialogRef.close(true);
|
||||
} catch (error: any) {
|
||||
this._toaster.error(_('clone-dossier-template.error.generic'), { params: error });
|
||||
}
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
private _getCloneName(): string | null {
|
||||
const templateName = this._dossierTemplate.name.trim();
|
||||
|
||||
let nameOfClonedTemplate: string = templateName.split('Clone of ').filter(n => n)[0];
|
||||
nameOfClonedTemplate = nameOfClonedTemplate.split(/\(\s*\d+\s*\)$/)[0].trim();
|
||||
|
||||
const allTemplatesNames = this._dossierTemplatesService.all.map(t => t.name);
|
||||
|
||||
let clonesCount = 0;
|
||||
for (const name of allTemplatesNames) {
|
||||
const splitName = name.split(nameOfClonedTemplate);
|
||||
const suffixRegExp = new RegExp(/^\(\s*\d+\s*\)$/);
|
||||
if (splitName[0] === 'Clone of ' && (splitName[1].trim().match(suffixRegExp) || splitName[1] === '')) {
|
||||
clonesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (clonesCount >= 1) {
|
||||
return `Clone of ${nameOfClonedTemplate} ${clonesCount === 1 ? '(1)' : `(${clonesCount})`}`;
|
||||
}
|
||||
return `Clone of ${nameOfClonedTemplate}`;
|
||||
}
|
||||
}
|
||||
@ -61,7 +61,7 @@ export class DossierTemplatesListingScreenComponent extends ListingComponent<Dos
|
||||
}
|
||||
|
||||
openAddDossierTemplateDialog() {
|
||||
this._dialogService.openDialog('addEditDossierTemplate', null, null);
|
||||
this._dialogService.openDialog('addEditCloneDossierTemplate', null, null);
|
||||
}
|
||||
|
||||
private async _deleteTemplates(templateIds = this.listingService.selected.map(d => d.dossierTemplateId)) {
|
||||
|
||||
@ -31,6 +31,6 @@ export class DossierTemplateInfoScreenComponent {
|
||||
}
|
||||
|
||||
openEditDossierTemplateDialog($event: MouseEvent, dossierTemplate: DossierTemplate) {
|
||||
this._dialogService.openDialog('addEditDossierTemplate', $event, { dossierTemplateId: dossierTemplate.id });
|
||||
this._dialogService.openDialog('addEditCloneDossierTemplate', $event, { dossierTemplateId: dossierTemplate.id });
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { LICENSE_STORAGE_KEY } from '../utils/constants';
|
||||
import dayjs from 'dayjs';
|
||||
import { ILicenseReport } from '@red/domain';
|
||||
import { LicenseService } from '../services/license.service';
|
||||
import { IDateRange } from '../utils/date-range';
|
||||
import { ILicense } from '../utils/license';
|
||||
import { IDateRange, ILicense, ILicenseReport } from '@red/domain';
|
||||
import { LicenseService } from '../../../../../services/license.service';
|
||||
import { switchMap, tap } from 'rxjs/operators';
|
||||
import { List, LoadingService } from '@iqser/common-ui';
|
||||
import { generateDateRanges, isCurrentMonth, toDate, verboseDate } from '../utils/functions';
|
||||
@ -35,7 +33,7 @@ export class LicenseChartComponent {
|
||||
const startYear: number = startDate.year();
|
||||
|
||||
const dateRanges = generateDateRanges(startMonth, startYear, endDate.month() as number, endDate.year() as number);
|
||||
const reports = await this.#getReports(dateRanges);
|
||||
const reports = await this.#getReports(dateRanges, license.id);
|
||||
|
||||
return this.#mapRangesToReports(dateRanges, reports);
|
||||
}
|
||||
@ -52,12 +50,12 @@ export class LicenseChartComponent {
|
||||
]);
|
||||
}
|
||||
|
||||
#getReports(dateRanges: List<IDateRange>) {
|
||||
#getReports(dateRanges: List<IDateRange>, id: string) {
|
||||
const reports = dateRanges.map(range => {
|
||||
const startMonth = range.startMonth + 1;
|
||||
const endMonth = range.endMonth + 1;
|
||||
|
||||
const key = `${startMonth}.${range.startYear}-${endMonth}.${range.endYear}`;
|
||||
const key = `${id}-${startMonth}.${range.startYear}-${endMonth}.${range.endYear}`;
|
||||
const existingReport = this._licenseService.storedReports[key];
|
||||
if (existingReport) {
|
||||
return existingReport;
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
<div class="content-inner">
|
||||
<div class="content-container">
|
||||
<div class="grid-container">
|
||||
<div *ngIf="licenseService.licenseData$ | async" class="grid-container">
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.backend-version"></div>
|
||||
<div>{{ configService.values.BACKEND_APP_VERSION || '-' }}</div>
|
||||
@ -34,7 +34,7 @@
|
||||
|
||||
<div class="section-title all-caps-label" translate="license-info-screen.licensing-details"></div>
|
||||
|
||||
<div *ngIf="userPreferenceService.areDevFeaturesEnabled" class="row">
|
||||
<div class="row">
|
||||
<div class="flex align-center" translate="license-info-screen.license-title"></div>
|
||||
<div>
|
||||
<redaction-license-select (valueChanges)="licenseChanged($event)"></redaction-license-select>
|
||||
|
||||
@ -5,11 +5,11 @@ import { ButtonConfig, IconButtonTypes, LoadingService } from '@iqser/common-ui'
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
import { LicenseService } from '../services/license.service';
|
||||
import { ILicenseReport } from '@red/domain';
|
||||
import { LicenseService } from '../../../../../services/license.service';
|
||||
import { ILicense, ILicenseReport } from '@red/domain';
|
||||
import dayjs from 'dayjs';
|
||||
import { ILicense } from '../utils/license';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
templateUrl: './license-screen.component.html',
|
||||
@ -30,7 +30,7 @@ export class LicenseScreenComponent implements OnInit {
|
||||
totalInfo: ILicenseReport = {};
|
||||
unlicensedInfo: ILicenseReport = {};
|
||||
analysisPercentageOfLicense = 100;
|
||||
totalLicensedNumberOfPages = this.licenseService.processingPages;
|
||||
totalLicensedNumberOfPages: number;
|
||||
|
||||
constructor(
|
||||
readonly configService: ConfigService,
|
||||
@ -44,8 +44,10 @@ export class LicenseScreenComponent implements OnInit {
|
||||
_loadingService.start();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
return this.loadLicenseData(this.licenseService.selectedLicense);
|
||||
async ngOnInit() {
|
||||
await firstValueFrom(this.licenseService.loadLicense());
|
||||
this.totalLicensedNumberOfPages = this.licenseService.processingPages;
|
||||
await this.loadLicenseData(this.licenseService.selectedLicense);
|
||||
}
|
||||
|
||||
async loadLicenseData(license: ILicense) {
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<div class="iqser-input-group w-400">
|
||||
<mat-select (valueChange)="licenseChanged($event)" [(ngModel)]="value">
|
||||
<div *ngIf="licenses$ | async as licenses" class="iqser-input-group w-400">
|
||||
<mat-select (valueChange)="licenseChanged($event)" *ngIf="value" [(ngModel)]="value">
|
||||
<mat-select-trigger>
|
||||
<ng-container *ngTemplateOutlet="licenseInfo; context: { license: value }"></ng-container>
|
||||
</mat-select-trigger>
|
||||
|
||||
<mat-option *ngFor="let license of licenseService.licenseData.licenses" [value]="license">
|
||||
<mat-option *ngFor="let license of licenses" [value]="license">
|
||||
<ng-container *ngTemplateOutlet="licenseInfo; context: { license: this.license }"></ng-container>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { Component, EventEmitter, Output } from '@angular/core';
|
||||
import { LicenseService } from '../services/license.service';
|
||||
import { ILicense } from '../utils/license';
|
||||
import { LicenseService } from '../../../../../services/license.service';
|
||||
import { ILicense } from '@red/domain';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
|
||||
const translations = {
|
||||
active: _('license-info-screen.status.active'),
|
||||
@ -15,8 +16,15 @@ const translations = {
|
||||
})
|
||||
export class LicenseSelectComponent {
|
||||
@Output() readonly valueChanges = new EventEmitter<ILicense>();
|
||||
|
||||
value = this.licenseService.getActiveLicense();
|
||||
value: ILicense;
|
||||
licenses$ = this.licenseService.licenseData$.pipe(
|
||||
map(data => data.licenses),
|
||||
tap(() => {
|
||||
if (!this.value) {
|
||||
this.value = this.licenseService.activeLicense;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
constructor(readonly licenseService: LicenseService) {}
|
||||
|
||||
@ -26,6 +34,6 @@ export class LicenseSelectComponent {
|
||||
|
||||
licenseChanged($event: ILicense) {
|
||||
this.valueChanges.emit($event);
|
||||
this.licenseService.selectedLicense$.next($event);
|
||||
this.licenseService.setSelectedLicense($event);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
||||
import { LicenseScreenComponent } from './license-screen/license-screen.component';
|
||||
import { LicenseSelectComponent } from './license-select/license-select.component';
|
||||
import { LicenseChartComponent } from './license-chart/license-chart.component';
|
||||
import { LicenseService } from './services/license.service';
|
||||
import { LicenseService } from '../../../../services/license.service';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
@ -1,81 +0,0 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { GenericService, QueryParam, RequiredParam, Validate } from '@iqser/common-ui';
|
||||
import { ILicenseReport, ILicenseReportRequest } from '@red/domain';
|
||||
import { BehaviorSubject, firstValueFrom } from 'rxjs';
|
||||
import { ILicense, ILicenses, LICENSE_DATA } from '../utils/license';
|
||||
import { ConfigService } from '../../../../../services/config.service';
|
||||
import dayjs from 'dayjs';
|
||||
import { getStoredReports } from '../utils/functions';
|
||||
|
||||
@Injectable()
|
||||
export class LicenseService extends GenericService<ILicenseReport> {
|
||||
storedReports = getStoredReports();
|
||||
readonly licenseData = this.#licenseData;
|
||||
readonly activeLicenseId = this.licenseData.activeLicense;
|
||||
readonly selectedLicense$ = new BehaviorSubject<ILicense>(this.getActiveLicense());
|
||||
|
||||
constructor(protected readonly _injector: Injector, private readonly _configService: ConfigService) {
|
||||
super(_injector, 'report');
|
||||
}
|
||||
|
||||
get selectedLicense() {
|
||||
return this.selectedLicense$.value;
|
||||
}
|
||||
|
||||
get processingPages() {
|
||||
const processingPagesFeature = this.selectedLicense$.value.features.find(f => f.name === 'processingPages');
|
||||
return Number(processingPagesFeature.value ?? '0');
|
||||
}
|
||||
|
||||
get #licenseData(): ILicenses {
|
||||
return {
|
||||
...LICENSE_DATA,
|
||||
licenses: [
|
||||
...LICENSE_DATA.licenses,
|
||||
{
|
||||
id: 'guid-0',
|
||||
name: this._configService.values.LICENSE_CUSTOMER,
|
||||
product: 'RedactManager',
|
||||
licensedTo: this._configService.values.LICENSE_CUSTOMER,
|
||||
licensedToEmail: this._configService.values.LICENSE_EMAIL,
|
||||
validFrom: dayjs(this._configService.values.LICENSE_START, 'DD-MM-YYYY').toISOString(),
|
||||
validUntil: dayjs(this._configService.values.LICENSE_END, 'DD-MM-YYYY').toISOString(),
|
||||
features: [
|
||||
{
|
||||
name: 'pdftron',
|
||||
type: 'STRING',
|
||||
value: 'base64 encoded pdftron webviewer license key',
|
||||
},
|
||||
{
|
||||
name: 'processingPages',
|
||||
type: 'NUMBER',
|
||||
value: this._configService.values.LICENSE_PAGE_COUNT.toString(),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
getActiveLicense() {
|
||||
return this.licenseData.licenses.find(license => license.id === this.activeLicenseId);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
getReport$(@RequiredParam() body: ILicenseReportRequest, limit?: number, offset?: number) {
|
||||
const queryParams: QueryParam[] = [];
|
||||
if (limit) {
|
||||
queryParams.push({ key: 'limit', value: limit });
|
||||
}
|
||||
|
||||
if (offset) {
|
||||
queryParams.push({ key: 'offset', value: offset });
|
||||
}
|
||||
|
||||
return this._post(body, `${this._defaultModelPath}/license`, queryParams);
|
||||
}
|
||||
|
||||
getReport(body: ILicenseReportRequest, limit?: number, offset?: number) {
|
||||
return firstValueFrom(this.getReport$(body, limit, offset));
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,5 @@
|
||||
import dayjs from 'dayjs';
|
||||
import { IDateRange } from './date-range';
|
||||
import { LICENSE_STORAGE_KEY } from './constants';
|
||||
import { ILicenseReport } from '@red/domain';
|
||||
import { IDateRange } from '@red/domain';
|
||||
|
||||
export function toDate(month: number, year: number) {
|
||||
return dayjs(`01-${month}-${year}`, 'DD-M-YYYY').toDate();
|
||||
@ -27,11 +25,6 @@ export function generateDateRanges(month: number, year: number, endMonth: number
|
||||
return dates;
|
||||
}
|
||||
|
||||
export function getStoredReports() {
|
||||
const rawStoredReports = localStorage.getItem(LICENSE_STORAGE_KEY);
|
||||
return JSON.parse(rawStoredReports ?? '{}') as Record<string, ILicenseReport>;
|
||||
}
|
||||
|
||||
export function isCurrentMonth(month: number, year: number) {
|
||||
const now = dayjs();
|
||||
const currentMonth = now.month() + 1;
|
||||
|
||||
@ -1,69 +0,0 @@
|
||||
export interface ILicenseFeature {
|
||||
readonly name: string;
|
||||
readonly type: string;
|
||||
readonly value: string;
|
||||
}
|
||||
|
||||
export interface ILicense {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly product: string;
|
||||
readonly licensedTo: string;
|
||||
readonly licensedToEmail: string;
|
||||
readonly validFrom: string;
|
||||
readonly validUntil: string;
|
||||
readonly features: readonly ILicenseFeature[];
|
||||
}
|
||||
|
||||
export interface ILicenses {
|
||||
readonly activeLicense: string;
|
||||
readonly licenses: readonly ILicense[];
|
||||
}
|
||||
|
||||
export const LICENSE_DATA: ILicenses = {
|
||||
activeLicense: 'guid-0',
|
||||
licenses: [
|
||||
{
|
||||
id: 'guid-1',
|
||||
name: '1 Year cumulative (2022)',
|
||||
product: 'RedactManager',
|
||||
licensedTo: 'Customer company name 1',
|
||||
licensedToEmail: 'customer@example.com',
|
||||
validFrom: '2022-01-01T00:00:00.000Z',
|
||||
validUntil: '2022-12-31T23:59:59.999Z',
|
||||
features: [
|
||||
{
|
||||
name: 'pdftron',
|
||||
type: 'STRING',
|
||||
value: 'base64 encoded pdftron webviewer license key',
|
||||
},
|
||||
{
|
||||
name: 'processingPages',
|
||||
type: 'NUMBER',
|
||||
value: '200000',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'guid-2',
|
||||
name: '2 Year cumulative (2021)',
|
||||
product: 'RedactManager',
|
||||
licensedTo: 'Customer company name 2',
|
||||
licensedToEmail: 'customer@example.com',
|
||||
validFrom: '2021-01-01T00:00:00.000Z',
|
||||
validUntil: '2021-12-31T23:59:59.999Z',
|
||||
features: [
|
||||
{
|
||||
name: 'pdftron',
|
||||
type: 'STRING',
|
||||
value: 'base64 encoded pdftron webviewer license key',
|
||||
},
|
||||
{
|
||||
name: 'processingPages',
|
||||
type: 'NUMBER',
|
||||
value: '100000',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -1,18 +1,18 @@
|
||||
import { ChangeDetectorRef, Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import WebViewer, { WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { environment } from '@environments/environment';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Debounce, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { DOSSIER_TEMPLATE_ID, IWatermark, WatermarkOrientation, WatermarkOrientations } from '@red/domain';
|
||||
import { BASE_HREF, BASE_HREF_FN, BaseHrefFn } from '../../../../../tokens';
|
||||
import { BASE_HREF_FN, BaseHrefFn } from '../../../../../tokens';
|
||||
import { stampPDFPage } from '@utils/page-stamper';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { WatermarkService } from '@services/entity-services/watermark.service';
|
||||
import { firstValueFrom, Observable, of, switchMap } from 'rxjs';
|
||||
import { catchError, tap } from 'rxjs/operators';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { LicenseService } from '../../../../../services/license.service';
|
||||
|
||||
export const DEFAULT_WATERMARK: IWatermark = {
|
||||
text: null,
|
||||
@ -44,6 +44,7 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _licenseService: LicenseService,
|
||||
@Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn,
|
||||
private readonly _watermarkService: WatermarkService,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
@ -125,7 +126,7 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
if (!this._instance) {
|
||||
WebViewer(
|
||||
{
|
||||
licenseKey: environment.licenseKey ? atob(environment.licenseKey) : null,
|
||||
licenseKey: this._licenseService.activeLicenseKey,
|
||||
path: this._convertPath('/assets/wv-resources'),
|
||||
css: this._convertPath('/assets/pdftron/stylesheet.css'),
|
||||
fullAPI: true,
|
||||
@ -169,7 +170,18 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
const opacity: number = this.form.get('opacity').value;
|
||||
const color: string = this.form.get('hexColor').value;
|
||||
|
||||
await stampPDFPage(document, pdfNet, text, fontSize, fontType, orientation, opacity, color, [1]);
|
||||
await stampPDFPage(
|
||||
document,
|
||||
pdfNet,
|
||||
text,
|
||||
fontSize,
|
||||
fontType,
|
||||
orientation,
|
||||
opacity,
|
||||
color,
|
||||
[1],
|
||||
this._licenseService.activeLicenseKey,
|
||||
);
|
||||
this._instance.Core.documentViewer.refreshAll();
|
||||
this._instance.Core.documentViewer.updateView([0], 0);
|
||||
this._changeDetectorRef.detectChanges();
|
||||
|
||||
@ -2,7 +2,7 @@ import { Injectable, TemplateRef } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { AddEditFileAttributeDialogComponent } from '../dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component';
|
||||
import { AddEntityDialogComponent } from '../dialogs/add-entity-dialog/add-entity-dialog.component';
|
||||
import { AddEditDossierTemplateDialogComponent } from '../dialogs/add-edit-dossier-template-dialog/add-edit-dossier-template-dialog.component';
|
||||
import { AddEditCloneDossierTemplateDialogComponent } from '../dialogs/add-edit-dossier-template-dialog/add-edit-clone-dossier-template-dialog.component';
|
||||
import { EditColorDialogComponent } from '../dialogs/edit-color-dialog/edit-color-dialog.component';
|
||||
import { SmtpAuthDialogComponent } from '../dialogs/smtp-auth-dialog/smtp-auth-dialog.component';
|
||||
import { AddEditUserDialogComponent } from '../dialogs/add-edit-user-dialog/add-edit-user-dialog.component';
|
||||
@ -28,7 +28,6 @@ import { ActiveDossiersService } from '@services/dossiers/active-dossiers.servic
|
||||
import { UserService } from '@services/user.service';
|
||||
import { IDossierAttributeConfig, IFileAttributeConfig, IReportTemplate } from '@red/domain';
|
||||
import { ReportTemplateService } from '@services/report-template.service';
|
||||
import { CloneDossierTemplateDialogComponent } from '../dialogs/clone-dossier-template-dialog/clone-dossier-template-dialog.component';
|
||||
import { ConfigureCertificateDialogComponent } from '../dialogs/configure-digital-signature-dialog/configure-certificate-dialog.component';
|
||||
|
||||
type DialogType =
|
||||
@ -40,8 +39,7 @@ type DialogType =
|
||||
| 'fileAttributesConfigurations'
|
||||
| 'addEditUser'
|
||||
| 'smtpAuthConfig'
|
||||
| 'addEditDossierTemplate'
|
||||
| 'cloneDossierTemplate'
|
||||
| 'addEditCloneDossierTemplate'
|
||||
| 'addEditDossierAttribute'
|
||||
| 'uploadDictionary'
|
||||
| 'addEditDossierState'
|
||||
@ -82,14 +80,10 @@ export class AdminDialogService extends DialogService<DialogType> {
|
||||
component: SmtpAuthDialogComponent,
|
||||
dialogConfig: { autoFocus: true },
|
||||
},
|
||||
addEditDossierTemplate: {
|
||||
component: AddEditDossierTemplateDialogComponent,
|
||||
addEditCloneDossierTemplate: {
|
||||
component: AddEditCloneDossierTemplateDialogComponent,
|
||||
dialogConfig: { width: '900px', autoFocus: true },
|
||||
},
|
||||
cloneDossierTemplate: {
|
||||
component: CloneDossierTemplateDialogComponent,
|
||||
dialogConfig: { disableClose: false },
|
||||
},
|
||||
addEditDossierAttribute: {
|
||||
component: AddEditDossierAttributeDialogComponent,
|
||||
dialogConfig: { autoFocus: true },
|
||||
|
||||
@ -7,14 +7,14 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="openCloneDossierTemplateDialog($event)"
|
||||
(action)="openEditCloneDossierTemplateDialog($event, true)"
|
||||
[tooltip]="'dossier-templates-listing.action.clone' | translate"
|
||||
icon="iqser:copy"
|
||||
[type]="circleButtonTypes.dark"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="openEditDossierTemplateDialog($event)"
|
||||
(action)="openEditCloneDossierTemplateDialog($event)"
|
||||
[tooltip]="'dossier-templates-listing.action.edit' | translate"
|
||||
icon="iqser:edit"
|
||||
[type]="circleButtonTypes.dark"
|
||||
|
||||
@ -31,12 +31,8 @@ export class DossierTemplateActionsComponent implements OnInit {
|
||||
this.dossierTemplateId ??= this._route.snapshot.paramMap.get(DOSSIER_TEMPLATE_ID);
|
||||
}
|
||||
|
||||
openEditDossierTemplateDialog($event: MouseEvent) {
|
||||
this._dialogService.openDialog('addEditDossierTemplate', $event, this.dossierTemplateId);
|
||||
}
|
||||
|
||||
openCloneDossierTemplateDialog($event: MouseEvent) {
|
||||
this._dialogService.openDialog('cloneDossierTemplate', $event, this.dossierTemplateId);
|
||||
openEditCloneDossierTemplateDialog($event: MouseEvent, clone: boolean = false) {
|
||||
this._dialogService.openDialog('addEditCloneDossierTemplate', $event, { dossierTemplateId: this.dossierTemplateId, clone });
|
||||
}
|
||||
|
||||
openDeleteDossierTemplateDialog($event?: MouseEvent) {
|
||||
|
||||
@ -14,6 +14,8 @@
|
||||
[noDataText]="'archived-dossiers-listing.no-data.title' | translate"
|
||||
[noMatchText]="'archived-dossiers-listing.no-match.title' | translate"
|
||||
[tableColumnConfigs]="tableColumnConfigs"
|
||||
[hasScrollButton]="true"
|
||||
[helpModeKey]="'dossier'"
|
||||
noDataIcon="red:folder"
|
||||
></iqser-table>
|
||||
</div>
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
<div>
|
||||
<div
|
||||
[class.error]="file.isError"
|
||||
[class.initial-processing]="file.isInitialProcessing"
|
||||
[matTooltip]="file.filename"
|
||||
class="table-item-title"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
{{ file.filename }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="primaryAttribute" class="small-label">
|
||||
<div class="primary-attribute">
|
||||
<span [matTooltip]="primaryAttribute" matTooltipPosition="above">
|
||||
{{ primaryAttribute }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<redaction-file-stats [file]="file"></redaction-file-stats>
|
||||
@ -1,18 +0,0 @@
|
||||
@use 'common-mixins';
|
||||
|
||||
.table-item-title {
|
||||
max-width: 25vw;
|
||||
|
||||
&.error {
|
||||
color: var(--iqser-red-1);
|
||||
}
|
||||
|
||||
&.initial-processing {
|
||||
color: var(--iqser-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
.primary-attribute {
|
||||
padding-top: 6px;
|
||||
@include common-mixins.line-clamp(1);
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
|
||||
import { File } from '@red/domain';
|
||||
import { PrimaryFileAttributeService } from '@services/primary-file-attribute.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-name-column',
|
||||
templateUrl: './file-name-column.component.html',
|
||||
styleUrls: ['./file-name-column.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FileNameColumnComponent implements OnChanges {
|
||||
@Input() file: File;
|
||||
@Input() dossierTemplateId: string;
|
||||
primaryAttribute: string;
|
||||
|
||||
constructor(private readonly _primaryFileAttributeService: PrimaryFileAttributeService) {}
|
||||
|
||||
ngOnChanges() {
|
||||
this.primaryAttribute = this._primaryFileAttributeService.getPrimaryFileAttributeValue(this.file, this.dossierTemplateId);
|
||||
}
|
||||
}
|
||||
@ -24,7 +24,7 @@
|
||||
[showNoDataButton]="permissionsService.canUploadFiles(dossier)"
|
||||
[tableColumnConfigs]="tableColumnConfigs"
|
||||
[tableItemClasses]="{ disabled: disabledFn, 'last-opened': lastOpenedFn }"
|
||||
iqserHelpMode="document-list"
|
||||
[helpModeKey]="'documents'"
|
||||
></iqser-table>
|
||||
|
||||
<iqser-workflow
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
[noMatchText]="'dossier-listing.no-match.title' | translate"
|
||||
[showNoDataButton]="permissionsService.canCreateDossier()"
|
||||
[tableColumnConfigs]="tableColumnConfigs"
|
||||
[helpModeKey]="'dossier'"
|
||||
noDataIcon="red:folder"
|
||||
></iqser-table>
|
||||
</div>
|
||||
|
||||
@ -9,6 +9,7 @@ import { firstValueFrom } from 'rxjs';
|
||||
import { WatermarkService } from '@services/entity-services/watermark.service';
|
||||
import { PdfViewer } from '../../pdf-viewer/services/pdf-viewer.service';
|
||||
import { REDDocumentViewer } from '../../pdf-viewer/services/document-viewer.service';
|
||||
import { LicenseService } from '../../../services/license.service';
|
||||
import PDFNet = Core.PDFNet;
|
||||
|
||||
@Injectable()
|
||||
@ -21,6 +22,7 @@ export class StampService {
|
||||
private readonly _viewModeService: ViewModeService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _watermarkService: WatermarkService,
|
||||
private readonly _licenseService: LicenseService,
|
||||
) {}
|
||||
|
||||
async stampPDF(): Promise<void> {
|
||||
@ -33,7 +35,7 @@ export class StampService {
|
||||
const allPages = [...Array(file.numberOfPages).keys()].map(page => page + 1);
|
||||
|
||||
try {
|
||||
await clearStamps(pdfDoc, this._pdf.PDFNet, allPages);
|
||||
await clearStamps(pdfDoc, this._pdf.PDFNet, allPages, this._licenseService.activeLicenseKey);
|
||||
} catch (e) {
|
||||
console.error('Error clearing stamps: ', e);
|
||||
return;
|
||||
@ -63,6 +65,7 @@ export class StampService {
|
||||
50,
|
||||
'#dd4d50',
|
||||
excludedPages,
|
||||
this._licenseService.activeLicenseKey,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -79,6 +82,7 @@ export class StampService {
|
||||
watermark.opacity,
|
||||
watermark.hexColor,
|
||||
Array.from({ length: await document.getPageCount() }, (x, i) => i + 1),
|
||||
this._licenseService.activeLicenseKey,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ export const processPage = async (
|
||||
document2: Core.PDFNet.PDFDoc,
|
||||
mergedDocument: Core.PDFNet.PDFDoc,
|
||||
pdfNet: typeof Core.PDFNet,
|
||||
licenseKey: string,
|
||||
) => {
|
||||
const document1PageCount = await document1.getPageCount();
|
||||
|
||||
@ -19,7 +20,16 @@ export const processPage = async (
|
||||
const blankPage = await mergedDocument.pageCreate(await pageToCopy.getCropBox());
|
||||
await blankPage.setRotation(await pageToCopy.getRotation());
|
||||
await mergedDocument.pagePushBack(blankPage);
|
||||
await stampPDFPage(mergedDocument, pdfNet, '<< Compare Placeholder Page >>', 20, 'courier', 'DIAGONAL', 33, '#ffb83b', [
|
||||
await mergedDocument.getPageCount(),
|
||||
]);
|
||||
await stampPDFPage(
|
||||
mergedDocument,
|
||||
pdfNet,
|
||||
'<< Compare Placeholder Page >>',
|
||||
20,
|
||||
'courier',
|
||||
'DIAGONAL',
|
||||
33,
|
||||
'#ffb83b',
|
||||
[await mergedDocument.getPageCount()],
|
||||
licenseKey,
|
||||
);
|
||||
};
|
||||
|
||||
@ -12,6 +12,7 @@ import { firstValueFrom } from 'rxjs';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { processPage } from '../../../file-preview/utils/compare-mode.utils';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { LicenseService } from '@services/license.service';
|
||||
import PDFDoc = Core.PDFNet.PDFDoc;
|
||||
|
||||
@Component({
|
||||
@ -28,6 +29,7 @@ export class CompareFileInputComponent {
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _documentViewer: REDDocumentViewer,
|
||||
private readonly _filesMapService: FilesMapService,
|
||||
private readonly _licenseService: LicenseService,
|
||||
private readonly _dialogService: SharedDialogService,
|
||||
private readonly _viewerHeaderService: ViewerHeaderService,
|
||||
) {}
|
||||
@ -113,8 +115,8 @@ export class CompareFileInputComponent {
|
||||
const maxPageCount = Math.max(await current.getPageCount(), await compare.getPageCount());
|
||||
|
||||
for (let idx = 1; idx <= maxPageCount; idx++) {
|
||||
await processPage(idx, current, compare, merged, pdfNet);
|
||||
await processPage(idx, compare, current, merged, pdfNet);
|
||||
await processPage(idx, current, compare, merged, pdfNet, this._licenseService.activeLicenseKey);
|
||||
await processPage(idx, compare, current, merged, pdfNet, this._licenseService.activeLicenseKey);
|
||||
}
|
||||
|
||||
const buffer = await merged.saveMemoryBuffer(pdfNet.SDFDoc.SaveOptions.e_linearized);
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { Inject, Injectable, Injector } from '@angular/core';
|
||||
import WebViewer, { Core, WebViewerInstance, WebViewerOptions } from '@pdftron/webviewer';
|
||||
import { environment } from '@environments/environment';
|
||||
import { BASE_HREF_FN, BaseHrefFn } from '../../../tokens';
|
||||
import { File, IHeaderElement } from '@red/domain';
|
||||
import { ErrorService, shareDistinctLast } from '@iqser/common-ui';
|
||||
@ -14,6 +13,7 @@ import { Rgb } from '../utils/types';
|
||||
import { asList } from '../utils/functions';
|
||||
import { REDAnnotationManager } from './annotation-manager.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { LicenseService } from '@services/license.service';
|
||||
import TextTool = Core.Tools.TextTool;
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
import TextHighlightAnnotation = Core.Annotations.TextHighlightAnnotation;
|
||||
@ -52,6 +52,7 @@ export class PdfViewer {
|
||||
private readonly _logger: NGXLogger,
|
||||
private readonly _injector: Injector,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _licenseService: LicenseService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _annotationManager: REDAnnotationManager,
|
||||
@Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn,
|
||||
@ -127,7 +128,8 @@ export class PdfViewer {
|
||||
|
||||
async init(htmlElement: HTMLElement) {
|
||||
this.#instance = await this.#getInstance(htmlElement);
|
||||
await this.PDFNet.initialize(environment.licenseKey ? window.atob(environment.licenseKey) : null);
|
||||
|
||||
await this.PDFNet.initialize(this._licenseService.activeLicenseKey);
|
||||
this._logger.info('[PDF] Initialized');
|
||||
|
||||
this.documentViewer = this.#instance.Core.documentViewer;
|
||||
@ -271,7 +273,7 @@ export class PdfViewer {
|
||||
|
||||
#getInstance(htmlElement: HTMLElement) {
|
||||
const options: WebViewerOptions = {
|
||||
licenseKey: environment.licenseKey ? window.atob(environment.licenseKey) : null,
|
||||
licenseKey: this._licenseService.activeLicenseKey,
|
||||
fullAPI: true,
|
||||
path: this._convertPath('/assets/wv-resources'),
|
||||
css: this._convertPath('/assets/pdftron/stylesheet.css'),
|
||||
|
||||
@ -76,7 +76,7 @@
|
||||
|
||||
<div class="cell small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon *ngIf="item.isArchived" svgIcon="red:archive"></mat-icon>
|
||||
<mat-icon *ngIf="item.archived" svgIcon="red:archive"></mat-icon>
|
||||
{{ item.dossierName }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -10,6 +10,7 @@ interface PartialFile {
|
||||
readonly excludedPages: number[];
|
||||
readonly lastOCRTime?: string;
|
||||
readonly fileAttributes: FileAttributes;
|
||||
readonly lastManualChangeDate?: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
||||
@ -2,23 +2,24 @@
|
||||
<div class="dialog-header heading-l" translate="overwrite-files-dialog.title"></div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<p [innerHTML]="'overwrite-files-dialog.question' | translate: { filename: filename }"></p>
|
||||
<p [innerHTML]="'overwrite-files-dialog.question' | translate: { filename: filename }" class="mb-24"></p>
|
||||
|
||||
<mat-checkbox (change)="remember = !remember" [checked]="remember" class="flex-1" color="primary">
|
||||
{{ 'overwrite-files-dialog.options.remember' | translate }}
|
||||
</mat-checkbox>
|
||||
<form [formGroup]="form">
|
||||
<iqser-details-radio [options]="options" formControlName="option"></iqser-details-radio>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button
|
||||
(click)="selectOption('overwrite')"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
translate="overwrite-files-dialog.options.overwrite"
|
||||
></button>
|
||||
<iqser-icon-button
|
||||
(action)="selectOption('skip')"
|
||||
[label]="'overwrite-files-dialog.options.skip' | translate"
|
||||
(action)="selectOption(false)"
|
||||
[disabled]="!form.valid"
|
||||
[label]="'overwrite-files-dialog.options.current-files' | translate"
|
||||
[type]="iconButtonTypes.primary"
|
||||
></iqser-icon-button>
|
||||
<iqser-icon-button
|
||||
(action)="selectOption(true)"
|
||||
[disabled]="!form.valid"
|
||||
[label]="'overwrite-files-dialog.options.all-files' | translate"
|
||||
[type]="iconButtonTypes.dark"
|
||||
></iqser-icon-button>
|
||||
<div (click)="cancel()" class="all-caps-label cancel" translate="overwrite-files-dialog.options.cancel"></div>
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { IconButtonTypes } from '@iqser/common-ui';
|
||||
import { DetailsRadioOption, IconButtonTypes } from '@iqser/common-ui';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { OverwriteFileOption, OverwriteFileOptions } from '@red/domain';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-overwrite-files-dialog',
|
||||
@ -10,19 +13,41 @@ import { IconButtonTypes } from '@iqser/common-ui';
|
||||
})
|
||||
export class OverwriteFilesDialogComponent {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
remember = false;
|
||||
readonly form: FormGroup;
|
||||
readonly options: DetailsRadioOption<OverwriteFileOption>[] = [
|
||||
{
|
||||
label: _('overwrite-files-dialog.options.full-overwrite.label'),
|
||||
value: OverwriteFileOptions.FULL_OVERWRITE,
|
||||
description: _('overwrite-files-dialog.options.full-overwrite.description'),
|
||||
},
|
||||
{
|
||||
label: _('overwrite-files-dialog.options.partial-overwrite.label'),
|
||||
value: OverwriteFileOptions.PARTIAL_OVERWRITE,
|
||||
description: _('overwrite-files-dialog.options.partial-overwrite.description'),
|
||||
},
|
||||
{
|
||||
label: _('overwrite-files-dialog.options.skip.label'),
|
||||
value: OverwriteFileOptions.SKIP,
|
||||
description: _('overwrite-files-dialog.options.skip.description'),
|
||||
},
|
||||
];
|
||||
|
||||
constructor(
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
public dialogRef: MatDialogRef<OverwriteFilesDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public filename: string,
|
||||
) {}
|
||||
) {
|
||||
this.form = this._formBuilder.group({
|
||||
option: [this.options[0], Validators.required],
|
||||
});
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
selectOption(option: 'overwrite' | 'skip') {
|
||||
this.dialogRef.close({ option, remember: this.remember });
|
||||
selectOption(remember: boolean) {
|
||||
this.dialogRef.close({ option: this.form.get('option').value.value, remember });
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,4 +9,5 @@ export interface FileUploadModel {
|
||||
typeError: boolean;
|
||||
dossierId?: string;
|
||||
dossierName?: string;
|
||||
keepManualRedactions?: boolean;
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import { HttpErrorResponse, HttpEventType, HttpStatusCode } from '@angular/commo
|
||||
import { interval, Subject, Subscription } from 'rxjs';
|
||||
import { ConfigService } from '@services/config.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { IFileUploadResult } from '@red/domain';
|
||||
import { IFileUploadResult, OverwriteFileOption, OverwriteFileOptions } from '@red/domain';
|
||||
import { isAcceptedFileType, isCsv } from '@utils/file-drop-utils';
|
||||
import { ErrorMessageService, GenericService, HeadersConfiguration, RequiredParam, Validate } from '@iqser/common-ui';
|
||||
import { FilesMapService } from '@services/files/files-map.service';
|
||||
@ -70,7 +70,7 @@ export class FileUploadService extends GenericService<IFileUploadResult> impleme
|
||||
const maxSizeMB = this._configService.values.MAX_FILE_SIZE_MB;
|
||||
const maxSizeBytes = maxSizeMB * 1024 * 1024;
|
||||
const dossierFiles = this._filesMapService.get(dossierId);
|
||||
let option: 'overwrite' | 'skip';
|
||||
let option: OverwriteFileOption;
|
||||
for (let idx = 0; idx < files.length; ++idx) {
|
||||
const file = files[idx];
|
||||
let currentOption = option;
|
||||
@ -84,7 +84,11 @@ export class FileUploadService extends GenericService<IFileUploadResult> impleme
|
||||
option = res.remember ? currentOption : undefined;
|
||||
}
|
||||
|
||||
if (currentOption === 'skip') {
|
||||
if (currentOption === OverwriteFileOptions.PARTIAL_OVERWRITE) {
|
||||
file.keepManualRedactions = true;
|
||||
}
|
||||
|
||||
if (currentOption === OverwriteFileOptions.SKIP) {
|
||||
files.splice(idx, 1);
|
||||
--idx;
|
||||
continue;
|
||||
@ -106,6 +110,7 @@ export class FileUploadService extends GenericService<IFileUploadResult> impleme
|
||||
file.sizeError = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.files.push(...files);
|
||||
files.forEach(newFile => {
|
||||
this._addFileToGroup(newFile);
|
||||
@ -139,9 +144,11 @@ export class FileUploadService extends GenericService<IFileUploadResult> impleme
|
||||
}
|
||||
|
||||
@Validate()
|
||||
uploadFileForm(@RequiredParam() dossierId: string, file?: Blob) {
|
||||
uploadFileForm(@RequiredParam() dossierId: string, keepManualRedactions = false, file?: Blob) {
|
||||
const formParams = new FormData();
|
||||
|
||||
formParams.append('keepManualRedactions', keepManualRedactions.toString());
|
||||
|
||||
if (file !== undefined) {
|
||||
formParams.append('file', file);
|
||||
}
|
||||
@ -190,7 +197,7 @@ export class FileUploadService extends GenericService<IFileUploadResult> impleme
|
||||
|
||||
private _createSubscription(uploadFile: FileUploadModel) {
|
||||
this.activeUploads++;
|
||||
const obs = this.uploadFileForm(uploadFile.dossierId, uploadFile.file);
|
||||
const obs = this.uploadFileForm(uploadFile.dossierId, uploadFile.keepManualRedactions, uploadFile.file);
|
||||
return obs.subscribe(
|
||||
event => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
|
||||
@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { OverwriteFilesDialogComponent } from '../dialogs/overwrite-files-dialog/overwrite-files-dialog.component';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { OverwriteFileOption } from '@red/domain';
|
||||
|
||||
const dialogConfig = {
|
||||
width: '662px',
|
||||
@ -13,7 +14,7 @@ const dialogConfig = {
|
||||
export class UploadDownloadDialogService {
|
||||
constructor(private readonly _dialog: MatDialog) {}
|
||||
|
||||
openOverwriteFileDialog(filename: string): Promise<{ option?: 'overwrite' | 'skip'; remember?: boolean; cancel?: boolean }> {
|
||||
openOverwriteFileDialog(filename: string): Promise<{ option?: OverwriteFileOption; remember?: boolean; cancel?: boolean }> {
|
||||
const ref = this._dialog.open(OverwriteFilesDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: filename,
|
||||
|
||||
88
apps/red-ui/src/app/services/license.service.ts
Normal file
88
apps/red-ui/src/app/services/license.service.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { GenericService, QueryParam, RequiredParam, Validate } from '@iqser/common-ui';
|
||||
import { ILicense, ILicenseReport, ILicenseReportRequest, ILicenses } from '@red/domain';
|
||||
import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs';
|
||||
import { ConfigService } from './config.service';
|
||||
import { filter, tap } from 'rxjs/operators';
|
||||
import { LICENSE_STORAGE_KEY } from '../modules/admin/screens/license/utils/constants';
|
||||
|
||||
export function getStoredReports() {
|
||||
const rawStoredReports = localStorage.getItem(LICENSE_STORAGE_KEY);
|
||||
return JSON.parse(rawStoredReports ?? '{}') as Record<string, ILicenseReport>;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LicenseService extends GenericService<ILicenseReport> {
|
||||
storedReports = getStoredReports();
|
||||
readonly licenseData$: Observable<ILicenses>;
|
||||
readonly selectedLicense$: Observable<ILicense>;
|
||||
activeLicenseId: string;
|
||||
readonly #licenseData$ = new BehaviorSubject<ILicenses | undefined>(undefined);
|
||||
readonly #selectedLicense$ = new BehaviorSubject<ILicense | undefined>(undefined);
|
||||
|
||||
constructor(protected readonly _injector: Injector, private readonly _configService: ConfigService) {
|
||||
super(_injector, 'report');
|
||||
this.selectedLicense$ = this.#selectedLicense$.pipe(filter(license => !!license));
|
||||
this.licenseData$ = this.#licenseData$.pipe(
|
||||
filter(licenses => !!licenses),
|
||||
tap(data => (this.activeLicenseId = data.activeLicense)),
|
||||
);
|
||||
}
|
||||
|
||||
get selectedLicense() {
|
||||
return this.#selectedLicense$.value;
|
||||
}
|
||||
|
||||
get processingPages() {
|
||||
const processingPagesFeature = this.#selectedLicense$.value?.features?.find(f => f.name === 'processingPages');
|
||||
return Number(processingPagesFeature?.value ?? '0');
|
||||
}
|
||||
|
||||
get activeLicense() {
|
||||
if (!this.#licenseData$.value) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { licenses, activeLicense: activeLicenseId } = this.#licenseData$.value;
|
||||
return licenses.find(license => license.id === activeLicenseId);
|
||||
}
|
||||
|
||||
get activeLicenseKey(): string {
|
||||
return this.activeLicense.features.find(f => f.name === 'pdftron').value;
|
||||
}
|
||||
|
||||
setDefaultSelectedLicense() {
|
||||
this.setSelectedLicense(this.activeLicense);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
getReport$(@RequiredParam() body: ILicenseReportRequest, limit?: number, offset?: number) {
|
||||
const queryParams: QueryParam[] = [];
|
||||
if (limit) {
|
||||
queryParams.push({ key: 'limit', value: limit });
|
||||
}
|
||||
|
||||
if (offset) {
|
||||
queryParams.push({ key: 'offset', value: offset });
|
||||
}
|
||||
|
||||
return this._post(body, `${this._defaultModelPath}/license`, queryParams);
|
||||
}
|
||||
|
||||
getReport(body: ILicenseReportRequest, limit?: number, offset?: number) {
|
||||
return firstValueFrom(this.getReport$(body, limit, offset));
|
||||
}
|
||||
|
||||
loadLicense() {
|
||||
return this._http.get<ILicenses>('/license').pipe(
|
||||
tap(licenses => this.#licenseData$.next(licenses)),
|
||||
tap(() => this.setDefaultSelectedLicense()),
|
||||
);
|
||||
}
|
||||
|
||||
setSelectedLicense($event: ILicense) {
|
||||
this.#selectedLicense$.next($event);
|
||||
}
|
||||
}
|
||||
@ -2,16 +2,19 @@ import { Injectable } from '@angular/core';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { filter } from 'rxjs/operators';
|
||||
|
||||
const LAST_DOSSIERS_SCREEN = 'routerHistory_lastDossiersScreen';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RouterHistoryService {
|
||||
private _lastDossiersScreen = '/';
|
||||
private _lastDossiersScreen = localStorage.getItem(LAST_DOSSIERS_SCREEN);
|
||||
|
||||
constructor(private readonly _router: Router) {
|
||||
this._router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe((event: NavigationEnd) => {
|
||||
if (event.url.includes('/dossiers') || event.url.includes('/archive')) {
|
||||
this._lastDossiersScreen = event.url;
|
||||
localStorage.setItem(LAST_DOSSIERS_SCREEN, this._lastDossiersScreen);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -24,4 +24,7 @@ export const processingFileStatusTranslations: { [key in ProcessingFileStatus]:
|
||||
REPROCESS: _('file-status.reprocess'),
|
||||
SURROUNDING_TEXT_PROCESSING: _('file-status.processing'),
|
||||
UNPROCESSED: _('file-status.unprocessed'),
|
||||
PRE_PROCESSING: _('file-status.full-processing'),
|
||||
PRE_PROCESSED: _('file-status.processed'),
|
||||
PRE_PROCESSING_FAILED: _('file-status.error'),
|
||||
};
|
||||
|
||||
@ -9,6 +9,7 @@ import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { FeaturesService } from '@services/features.service';
|
||||
import { SystemPreferencesService } from '@services/system-preferences.service';
|
||||
import { LicenseService } from '@services/license.service';
|
||||
|
||||
function lastDossierTemplateRedirect(baseHref: string, userPreferenceService: UserPreferenceService) {
|
||||
const url = window.location.href.split('/').filter(s => s.length > 0);
|
||||
@ -29,6 +30,7 @@ export function configurationInitializer(
|
||||
languageService: LanguageService,
|
||||
userService: UserService,
|
||||
userPreferenceService: UserPreferenceService,
|
||||
licenseService: LicenseService,
|
||||
) {
|
||||
return () =>
|
||||
firstValueFrom(
|
||||
@ -53,6 +55,7 @@ export function configurationInitializer(
|
||||
}),
|
||||
switchMap(() => languageService.chooseAndSetInitialLanguage()),
|
||||
tap(() => userService.initialize()),
|
||||
tap(() => firstValueFrom(licenseService.loadLicense())),
|
||||
take(1),
|
||||
),
|
||||
);
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { hexToRgb } from './functions';
|
||||
import { environment } from '@environments/environment';
|
||||
import { Core } from '@pdftron/webviewer';
|
||||
import PDFDoc = Core.PDFNet.PDFDoc;
|
||||
|
||||
@ -25,15 +24,12 @@ function convertFont(type: string): number {
|
||||
return 4;
|
||||
}
|
||||
|
||||
export async function clearStamps(document: PDFDoc, pdfNet: typeof Core.PDFNet, pages: number[]) {
|
||||
await pdfNet.runWithCleanup(
|
||||
async () => {
|
||||
await document.lock();
|
||||
const pageSet = await createPageSet(pdfNet, pages);
|
||||
await pdfNet.Stamper.deleteStamps(document, pageSet);
|
||||
},
|
||||
environment.licenseKey ? atob(environment.licenseKey) : null,
|
||||
);
|
||||
export async function clearStamps(document: PDFDoc, pdfNet: typeof Core.PDFNet, pages: number[], licenseKey: string) {
|
||||
await pdfNet.runWithCleanup(async () => {
|
||||
await document.lock();
|
||||
const pageSet = await createPageSet(pdfNet, pages);
|
||||
await pdfNet.Stamper.deleteStamps(document, pageSet);
|
||||
}, licenseKey);
|
||||
}
|
||||
|
||||
export async function stampPDFPage(
|
||||
@ -46,43 +42,41 @@ export async function stampPDFPage(
|
||||
opacity: number,
|
||||
color: string,
|
||||
pages: number[],
|
||||
licenseKey: string,
|
||||
) {
|
||||
await pdfNet.runWithCleanup(
|
||||
async () => {
|
||||
await document.lock();
|
||||
await pdfNet.runWithCleanup(async () => {
|
||||
await document.lock();
|
||||
|
||||
const pageSet = await createPageSet(pdfNet, pages);
|
||||
await pdfNet.Stamper.deleteStamps(document, pageSet);
|
||||
const pageSet = await createPageSet(pdfNet, pages);
|
||||
await pdfNet.Stamper.deleteStamps(document, pageSet);
|
||||
|
||||
const rgbColor = hexToRgb(color);
|
||||
const rgbColor = hexToRgb(color);
|
||||
|
||||
const stamper = await pdfNet.Stamper.create(3, fontSize, 0);
|
||||
await stamper.setFontColor(await pdfNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255));
|
||||
await stamper.setOpacity(opacity / 100);
|
||||
const stamper = await pdfNet.Stamper.create(3, fontSize, 0);
|
||||
await stamper.setFontColor(await pdfNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255));
|
||||
await stamper.setOpacity(opacity / 100);
|
||||
|
||||
switch (orientation) {
|
||||
case 'VERTICAL':
|
||||
await stamper.setAlignment(0, 0);
|
||||
await stamper.setRotation(-90);
|
||||
break;
|
||||
case 'HORIZONTAL':
|
||||
break;
|
||||
case 'TOP_LEFT':
|
||||
await stamper.setAlignment(-1, 1);
|
||||
await stamper.setRotation(90);
|
||||
await stamper.setPosition(20, 20);
|
||||
break;
|
||||
case 'DIAGONAL':
|
||||
default:
|
||||
await stamper.setAlignment(0, 0);
|
||||
await stamper.setRotation(-45);
|
||||
}
|
||||
switch (orientation) {
|
||||
case 'VERTICAL':
|
||||
await stamper.setAlignment(0, 0);
|
||||
await stamper.setRotation(-90);
|
||||
break;
|
||||
case 'HORIZONTAL':
|
||||
break;
|
||||
case 'TOP_LEFT':
|
||||
await stamper.setAlignment(-1, 1);
|
||||
await stamper.setRotation(90);
|
||||
await stamper.setPosition(20, 20);
|
||||
break;
|
||||
case 'DIAGONAL':
|
||||
default:
|
||||
await stamper.setAlignment(0, 0);
|
||||
await stamper.setRotation(-45);
|
||||
}
|
||||
|
||||
const font = await pdfNet.Font.createAndEmbed(document, convertFont(fontType));
|
||||
await stamper.setFont(font);
|
||||
await stamper.setTextAlignment(0);
|
||||
await stamper.stampText(document, text, pageSet);
|
||||
},
|
||||
environment.licenseKey ? atob(environment.licenseKey) : null,
|
||||
);
|
||||
const font = await pdfNet.Font.createAndEmbed(document, convertFont(fontType));
|
||||
await stamper.setFont(font);
|
||||
await stamper.setTextAlignment(0);
|
||||
await stamper.stampText(document, text, pageSet);
|
||||
}, licenseKey);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"ADMIN_CONTACT_NAME": null,
|
||||
"ADMIN_CONTACT_URL": null,
|
||||
"API_URL": "https://dev-05.iqser.cloud/redaction-gateway-v1",
|
||||
"API_URL": "https://dev-08.iqser.cloud/redaction-gateway-v1",
|
||||
"APP_NAME": "RedactManager",
|
||||
"AUTO_READ_TIME": 3,
|
||||
"BACKEND_APP_VERSION": "4.4.40",
|
||||
@ -16,7 +16,7 @@
|
||||
"MAX_RETRIES_ON_SERVER_ERROR": 3,
|
||||
"OAUTH_CLIENT_ID": "redaction",
|
||||
"OAUTH_IDP_HINT": null,
|
||||
"OAUTH_URL": "https://dev-05.iqser.cloud/auth/realms/redaction",
|
||||
"OAUTH_URL": "https://dev-08.iqser.cloud/auth/realms/redaction",
|
||||
"RECENT_PERIOD_IN_HOURS": 24,
|
||||
"SELECTION_MODE": "structural",
|
||||
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview"
|
||||
|
||||
@ -125,14 +125,8 @@
|
||||
"it": "",
|
||||
"fr": ""
|
||||
},
|
||||
"documents_scroll_up_button": {
|
||||
"en": "/en/index-en.html?contextId=documents_scroll_up_and_down",
|
||||
"de": "",
|
||||
"it": "",
|
||||
"fr": ""
|
||||
},
|
||||
"documents_scroll_down_button": {
|
||||
"en": "/en/index-en.html?contextId=documents_scroll_up_and_down",
|
||||
"scroll_documents_list": {
|
||||
"en": "/en/index-en.html?contextId=scroll_documents_list",
|
||||
"de": "",
|
||||
"it": "",
|
||||
"fr": ""
|
||||
|
||||
@ -36,6 +36,22 @@
|
||||
},
|
||||
"header-new": "Dossier erstellen"
|
||||
},
|
||||
"add-edit-clone-dossier-template": {
|
||||
"error": {
|
||||
"conflict": "Dossiervorlage konnte nicht erstellt werden: Es existiert bereits eine Dossiervorlage mit demselben Namen.",
|
||||
"generic": "Fehler beim Erstellen der Dossiervorlage."
|
||||
},
|
||||
"form": {
|
||||
"description": "Beschreibung",
|
||||
"description-placeholder": "Beschreibung eingeben",
|
||||
"name": "Name der Dossier-Vorlage",
|
||||
"name-placeholder": "Namen eingeben",
|
||||
"valid-from": "Gültig ab",
|
||||
"valid-to": "Gültig bis"
|
||||
},
|
||||
"save": "Dossier-Vorlage speichern",
|
||||
"title": "{type, select, edit{Dossier-Vorlage {name} bearbeiten} create{Dossier-Vorlage erstellen} clone{} other{}}"
|
||||
},
|
||||
"add-edit-dossier-attribute": {
|
||||
"error": {
|
||||
"generic": "Attribut konnte nicht gespeichert werden!"
|
||||
@ -61,22 +77,6 @@
|
||||
"success": "",
|
||||
"title": ""
|
||||
},
|
||||
"add-edit-dossier-template": {
|
||||
"error": {
|
||||
"conflict": "Dossiervorlage konnte nicht erstellt werden: Es existiert bereits eine Dossiervorlage mit demselben Namen.",
|
||||
"generic": "Fehler beim Erstellen der Dossiervorlage."
|
||||
},
|
||||
"form": {
|
||||
"description": "Beschreibung",
|
||||
"description-placeholder": "Beschreibung eingeben",
|
||||
"name": "Name der Dossier-Vorlage",
|
||||
"name-placeholder": "Namen eingeben",
|
||||
"valid-from": "Gültig ab",
|
||||
"valid-to": "Gültig bis"
|
||||
},
|
||||
"save": "Dossier-Vorlage speichern",
|
||||
"title": "{type, select, edit{Dossier-Vorlage {name} bearbeiten} create{Dossier-Vorlage erstellen} other{}}"
|
||||
},
|
||||
"add-edit-entity": {
|
||||
"error": {
|
||||
"entity-already-exists": "",
|
||||
@ -455,19 +455,6 @@
|
||||
},
|
||||
"header": "Begründung für die Schwärzung bearbeiten"
|
||||
},
|
||||
"clone-dossier-template": {
|
||||
"actions": {
|
||||
"save": ""
|
||||
},
|
||||
"content": {
|
||||
"name": "",
|
||||
"name-placeholder": ""
|
||||
},
|
||||
"error": {
|
||||
"generic": ""
|
||||
},
|
||||
"title": ""
|
||||
},
|
||||
"color": "",
|
||||
"comments": {
|
||||
"add-comment": "Kommentar eingeben",
|
||||
@ -577,6 +564,12 @@
|
||||
"title": "{count, plural, one{{justificationName}} other{ausgewählte Begründungen}} löschen"
|
||||
},
|
||||
"input-label": "Bitte geben Sie unten Folgendes ein, um fortzufahren",
|
||||
"keep-manual-redactions": {
|
||||
"confirmation-text": "",
|
||||
"deny-text": "",
|
||||
"question": "",
|
||||
"title": ""
|
||||
},
|
||||
"report-template-same-name": {
|
||||
"confirmation-text": "Ja. Hochladen fortsetzen",
|
||||
"deny-text": "Nein. Hochladen abbrechen",
|
||||
@ -1472,11 +1465,13 @@
|
||||
"labels": {
|
||||
"download-cleanup-download-files-hours": "",
|
||||
"download-cleanup-not-download-files-hours": "",
|
||||
"remove-digital-signature-on-upload": "",
|
||||
"soft-delete-cleanup-time": ""
|
||||
},
|
||||
"placeholders": {
|
||||
"download-cleanup-download-files-hours": "",
|
||||
"download-cleanup-not-download-files-hours": "",
|
||||
"remove-digital-signature-on-upload": "",
|
||||
"soft-delete-cleanup-time": ""
|
||||
},
|
||||
"title": ""
|
||||
@ -1700,10 +1695,21 @@
|
||||
},
|
||||
"overwrite-files-dialog": {
|
||||
"options": {
|
||||
"all-files": "",
|
||||
"cancel": "Alle Uploads abbrechen",
|
||||
"overwrite": "Vorhandenes Dokument ersetzen",
|
||||
"remember": "Auswahl speichern und nicht noch einmal fragen",
|
||||
"skip": "Vorhandenes Dokument behalten"
|
||||
"current-files": "",
|
||||
"full-overwrite": {
|
||||
"description": "",
|
||||
"label": ""
|
||||
},
|
||||
"partial-overwrite": {
|
||||
"description": "",
|
||||
"label": ""
|
||||
},
|
||||
"skip": {
|
||||
"description": "",
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"question": "<b>{filename}</b> ist bereits vorhanden. Wie möchten Sie fortfahren?",
|
||||
"title": "Das Dokument existiert bereits!"
|
||||
|
||||
@ -36,6 +36,22 @@
|
||||
},
|
||||
"header-new": "Create Dossier"
|
||||
},
|
||||
"add-edit-clone-dossier-template": {
|
||||
"error": {
|
||||
"conflict": "Failed to create dossier template: a dossier template with the same name already exists.",
|
||||
"generic": "Failed to create dossier template."
|
||||
},
|
||||
"form": {
|
||||
"description": "Description",
|
||||
"description-placeholder": "Enter Description",
|
||||
"name": "Dossier Template Name",
|
||||
"name-placeholder": "Enter Name",
|
||||
"valid-from": "Valid from",
|
||||
"valid-to": "Valid to"
|
||||
},
|
||||
"save": "Save Dossier Template",
|
||||
"title": "{type, select, edit{Edit {name}} create{Create} clone{Clone} other{}} Dossier Template"
|
||||
},
|
||||
"add-edit-dossier-attribute": {
|
||||
"error": {
|
||||
"generic": "Failed to save attribute!"
|
||||
@ -61,22 +77,6 @@
|
||||
"success": "Successfully {type, select, edit{updated} create{created} other{}} the dossier state!",
|
||||
"title": "{type, select, edit{Edit {name}} create{Create} other{}} Dossier State"
|
||||
},
|
||||
"add-edit-dossier-template": {
|
||||
"error": {
|
||||
"conflict": "Failed to create dossier template: a dossier template with the same name already exists.",
|
||||
"generic": "Failed to create dossier template."
|
||||
},
|
||||
"form": {
|
||||
"description": "Description",
|
||||
"description-placeholder": "Enter Description",
|
||||
"name": "Dossier Template Name",
|
||||
"name-placeholder": "Enter Name",
|
||||
"valid-from": "Valid from",
|
||||
"valid-to": "Valid to"
|
||||
},
|
||||
"save": "Save Dossier Template",
|
||||
"title": "{type, select, edit{Edit {name}} create{Create} other{}} Dossier Template"
|
||||
},
|
||||
"add-edit-entity": {
|
||||
"error": {
|
||||
"entity-already-exists": "Entity with this name already exists!",
|
||||
@ -455,19 +455,6 @@
|
||||
},
|
||||
"header": "Edit Redaction Reason"
|
||||
},
|
||||
"clone-dossier-template": {
|
||||
"actions": {
|
||||
"save": "Save Dossier Template"
|
||||
},
|
||||
"content": {
|
||||
"name": "Dossier Template Name",
|
||||
"name-placeholder": "Enter Name"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Failed to clone dossier template."
|
||||
},
|
||||
"title": "Clone Dossier Template"
|
||||
},
|
||||
"color": "Color",
|
||||
"comments": {
|
||||
"add-comment": "Enter comment",
|
||||
@ -577,6 +564,12 @@
|
||||
"title": "Delete {count, plural, one{{justificationName}} other{Selected Justifications}}"
|
||||
},
|
||||
"input-label": "To proceed please type below",
|
||||
"keep-manual-redactions": {
|
||||
"confirmation-text": "Keep",
|
||||
"deny-text": "Clear",
|
||||
"question": "At least one of the files you're overwriting has manual changes, do you wish to keep them?",
|
||||
"title": "Keep Manual Redactions?"
|
||||
},
|
||||
"report-template-same-name": {
|
||||
"confirmation-text": "Yes. Continue upload",
|
||||
"deny-text": "No. Cancel Upload",
|
||||
@ -1471,14 +1464,14 @@
|
||||
"labels": {
|
||||
"download-cleanup-download-files-hours": "Cleanup time for downloaded files (hours)",
|
||||
"download-cleanup-not-download-files-hours": "Cleanup time for not downloaded files (hours)",
|
||||
"soft-delete-cleanup-time": "Soft delete cleanup time (hours)",
|
||||
"remove-digital-signature-on-upload": "Remove Digital Signature on Upload"
|
||||
"remove-digital-signature-on-upload": "Remove Digital Signature on Upload",
|
||||
"soft-delete-cleanup-time": "Soft delete cleanup time (hours)"
|
||||
},
|
||||
"placeholders": {
|
||||
"download-cleanup-download-files-hours": "(hours)",
|
||||
"download-cleanup-not-download-files-hours": "(hours)",
|
||||
"soft-delete-cleanup-time": "(hours)",
|
||||
"remove-digital-signature-on-upload": "True / False"
|
||||
"remove-digital-signature-on-upload": "True / False",
|
||||
"soft-delete-cleanup-time": "(hours)"
|
||||
},
|
||||
"title": "System Preferences"
|
||||
},
|
||||
@ -1701,10 +1694,21 @@
|
||||
},
|
||||
"overwrite-files-dialog": {
|
||||
"options": {
|
||||
"cancel": "Cancel all uploads",
|
||||
"overwrite": "Replace existing document",
|
||||
"remember": "Remember choice and don't ask me again",
|
||||
"skip": "Keep existing document"
|
||||
"all-files": "Apply to all remaining files in current upload",
|
||||
"cancel": "Cancel upload",
|
||||
"current-files": "Apply to current file",
|
||||
"full-overwrite": {
|
||||
"description": "Manual changes done to the existing file will be removed and you are able to start over with redactions.",
|
||||
"label": "Overwrite and start over"
|
||||
},
|
||||
"partial-overwrite": {
|
||||
"description": "Manual changes are kept only if the affected redactions are still at the same position in the file. Some redactions could be misplaced if the content of the file changed.",
|
||||
"label": "Overwrite and keep manual changes"
|
||||
},
|
||||
"skip": {
|
||||
"description": "The upload will be skipped and the existing file will not be replaced.",
|
||||
"label": "Keep the existing file and do not overwrite"
|
||||
}
|
||||
},
|
||||
"question": "<b>{filename}</b> already exists. Choose how to proceed:",
|
||||
"title": "Document already exists!"
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit ea3c8b7f32be0e23c0b47a50bfda524d0b58107d
|
||||
Subproject commit 09dfcac064c6c496bb67cf13e3c5cd6d7819764c
|
||||
@ -24,3 +24,4 @@ export * from './lib/dossier-state';
|
||||
export * from './lib/trash';
|
||||
export * from './lib/text-highlight';
|
||||
export * from './lib/permissions';
|
||||
export * from './lib/license';
|
||||
|
||||
@ -104,7 +104,9 @@ export class File extends Entity<IFile> implements IFile {
|
||||
this.numberOfAnalyses = file.numberOfAnalyses;
|
||||
this.processingStatus = file.processingStatus;
|
||||
this.workflowStatus = file.workflowStatus;
|
||||
this.isError = this.processingStatus === ProcessingFileStatuses.ERROR;
|
||||
this.isError =
|
||||
this.processingStatus === ProcessingFileStatuses.ERROR ||
|
||||
this.processingStatus === ProcessingFileStatuses.PRE_PROCESSING_FAILED;
|
||||
this.isUnprocessed = this.processingStatus === ProcessingFileStatuses.UNPROCESSED;
|
||||
this.numberOfPages = this.isError ? 0 : file.numberOfPages ?? 0;
|
||||
this.rulesVersion = file.rulesVersion;
|
||||
|
||||
@ -2,3 +2,4 @@ export * from './file';
|
||||
export * from './file.model';
|
||||
export * from './types';
|
||||
export * from './file-upload-result';
|
||||
export * from './overwrite-file-options';
|
||||
|
||||
7
libs/red-domain/src/lib/files/overwrite-file-options.ts
Normal file
7
libs/red-domain/src/lib/files/overwrite-file-options.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export const OverwriteFileOptions = {
|
||||
FULL_OVERWRITE: 'FULL_OVERWRITE',
|
||||
PARTIAL_OVERWRITE: 'PARTIAL_OVERWRITE',
|
||||
SKIP: 'SKIP',
|
||||
} as const;
|
||||
|
||||
export type OverwriteFileOption = keyof typeof OverwriteFileOptions;
|
||||
@ -27,6 +27,9 @@ export const ProcessingFileStatuses = {
|
||||
REPROCESS: 'REPROCESS',
|
||||
SURROUNDING_TEXT_PROCESSING: 'SURROUNDING_TEXT_PROCESSING',
|
||||
UNPROCESSED: 'UNPROCESSED',
|
||||
PRE_PROCESSING: 'PRE_PROCESSING',
|
||||
PRE_PROCESSED: 'PRE_PROCESSED',
|
||||
PRE_PROCESSING_FAILED: 'PRE_PROCESSING_FAILED',
|
||||
} as const;
|
||||
|
||||
export type ProcessingFileStatus = keyof typeof ProcessingFileStatuses;
|
||||
@ -42,6 +45,7 @@ export const isProcessingStatuses: List<ProcessingFileStatus> = [
|
||||
ProcessingFileStatuses.PROCESSING,
|
||||
ProcessingFileStatuses.ANALYSE,
|
||||
ProcessingFileStatuses.FULL_PROCESSING,
|
||||
ProcessingFileStatuses.PRE_PROCESSING,
|
||||
] as const;
|
||||
|
||||
export const isFullProcessingStatuses: List<ProcessingFileStatus> = [
|
||||
@ -53,6 +57,7 @@ export const isFullProcessingStatuses: List<ProcessingFileStatus> = [
|
||||
ProcessingFileStatuses.NER_ANALYZING,
|
||||
ProcessingFileStatuses.OCR_PROCESSING,
|
||||
ProcessingFileStatuses.FULL_PROCESSING,
|
||||
ProcessingFileStatuses.PRE_PROCESSING,
|
||||
] as const;
|
||||
|
||||
export interface StatusBarConfig {
|
||||
|
||||
2
libs/red-domain/src/lib/license/index.ts
Normal file
2
libs/red-domain/src/lib/license/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './date-range';
|
||||
export * from './license';
|
||||
21
libs/red-domain/src/lib/license/license.ts
Normal file
21
libs/red-domain/src/lib/license/license.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export interface ILicenseFeature {
|
||||
readonly name: string;
|
||||
readonly type: string;
|
||||
readonly value: string;
|
||||
}
|
||||
|
||||
export interface ILicense {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly product: string;
|
||||
readonly licensedTo: string;
|
||||
readonly licensedToEmail: string;
|
||||
readonly validFrom: string;
|
||||
readonly validUntil: string;
|
||||
readonly features: readonly ILicenseFeature[];
|
||||
}
|
||||
|
||||
export interface ILicenses {
|
||||
readonly activeLicense: string;
|
||||
readonly licenses: readonly ILicense[];
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "redaction",
|
||||
"version": "3.506.0",
|
||||
"version": "3.514.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@ -49,12 +49,12 @@
|
||||
|
||||
/* Einleitung */
|
||||
.cat-panel-1:before {
|
||||
content: '\f277';
|
||||
content: '\f007';
|
||||
}
|
||||
|
||||
/* Workflow */
|
||||
.cat-panel-2:before {
|
||||
content: '\f0c1';
|
||||
content: '\f085';
|
||||
}
|
||||
|
||||
/* Voraussetzungen */
|
||||
|
||||
Binary file not shown.
@ -144,12 +144,12 @@
|
||||
|
||||
/* Einleitung */
|
||||
.cat-panel-1:before {
|
||||
content: '\f277';
|
||||
content: '\f007';
|
||||
}
|
||||
|
||||
/* Workflow */
|
||||
.cat-panel-2:before {
|
||||
content: '\f0c1';
|
||||
content: '\f085';
|
||||
}
|
||||
|
||||
/* Voraussetzungen */
|
||||
@ -440,7 +440,7 @@ main article {
|
||||
}
|
||||
|
||||
section {
|
||||
position: relative;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
section > .titlepage .title {
|
||||
@ -484,9 +484,9 @@ section > .titlepage .title {
|
||||
}
|
||||
|
||||
.checklist-reset-wrapper {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.warning,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user