Merge branch 'master' into VM/NotificationsPreferences
This commit is contained in:
commit
ad02be1d3c
@ -25,7 +25,6 @@ export class LanguageService {
|
||||
} else {
|
||||
defaultLang = 'en';
|
||||
}
|
||||
console.log(defaultLang);
|
||||
document.documentElement.lang = defaultLang;
|
||||
this._translateService.setDefaultLang(defaultLang);
|
||||
this._translateService.use(defaultLang).toPromise().then();
|
||||
|
||||
@ -12,6 +12,7 @@ export class AnnotationPermissions {
|
||||
canRejectSuggestion = true;
|
||||
canForceRedaction = true;
|
||||
canChangeLegalBasis = true;
|
||||
canResizeAnnotation = true;
|
||||
canRecategorizeImage = true;
|
||||
|
||||
static forUser(isApprover: boolean, user: User, annotations: AnnotationWrapper | AnnotationWrapper[]) {
|
||||
@ -41,6 +42,8 @@ export class AnnotationPermissions {
|
||||
|
||||
permissions.canRecategorizeImage = annotation.isImage;
|
||||
|
||||
permissions.canResizeAnnotation = annotation.isRedacted || annotation.isImage;
|
||||
|
||||
summedPermissions._merge(permissions);
|
||||
}
|
||||
return summedPermissions;
|
||||
|
||||
@ -4,14 +4,11 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { IComment, IPoint, IRectangle } from '@red/domain';
|
||||
|
||||
export type AnnotationSuperType =
|
||||
| 'add-dictionary'
|
||||
| 'remove-dictionary'
|
||||
| 'remove-only-here'
|
||||
| 'change-legal-basis'
|
||||
| 'suggestion-change-legal-basis'
|
||||
| 'suggestion-recategorize-image'
|
||||
| 'suggestion-add-dictionary'
|
||||
| 'suggestion-force-redaction'
|
||||
| 'suggestion-resize'
|
||||
| 'suggestion-remove-dictionary'
|
||||
| 'suggestion-add'
|
||||
| 'suggestion-remove'
|
||||
@ -20,7 +17,6 @@ export type AnnotationSuperType =
|
||||
| 'manual-redaction'
|
||||
| 'recommendation'
|
||||
| 'hint'
|
||||
| 'pending-analysis'
|
||||
| 'declined-suggestion';
|
||||
|
||||
export class AnnotationWrapper {
|
||||
@ -45,6 +41,7 @@ export class AnnotationWrapper {
|
||||
recommendationType: string;
|
||||
legalBasisValue: string;
|
||||
legalBasisChangeValue?: string;
|
||||
resizing?: boolean;
|
||||
|
||||
manual?: boolean;
|
||||
|
||||
@ -82,7 +79,7 @@ export class AnnotationWrapper {
|
||||
}
|
||||
|
||||
get isSuperTypeBasedColor() {
|
||||
return this.isSkipped || this.isSuggestion || this.isReadyForAnalysis || this.isDeclinedSuggestion;
|
||||
return this.isSkipped || this.isSuggestion || this.isDeclinedSuggestion;
|
||||
}
|
||||
|
||||
get isSkipped() {
|
||||
@ -120,16 +117,6 @@ export class AnnotationWrapper {
|
||||
return this.superType === 'declined-suggestion';
|
||||
}
|
||||
|
||||
get isReadyForAnalysis() {
|
||||
return (
|
||||
this.superType === 'add-dictionary' ||
|
||||
this.superType === 'remove-dictionary' ||
|
||||
this.superType === 'remove-only-here' ||
|
||||
this.superType === 'pending-analysis' ||
|
||||
this.superType === 'change-legal-basis'
|
||||
);
|
||||
}
|
||||
|
||||
get isApproved() {
|
||||
return this.status === 'APPROVED';
|
||||
}
|
||||
@ -171,7 +158,7 @@ export class AnnotationWrapper {
|
||||
}
|
||||
|
||||
get isConvertedRecommendation() {
|
||||
return this.isRecommendation && (this.superType === 'suggestion-add-dictionary' || this.superType === 'add-dictionary');
|
||||
return this.isRecommendation && this.superType === 'suggestion-add-dictionary';
|
||||
}
|
||||
|
||||
get isRecommendation() {
|
||||
@ -245,6 +232,13 @@ export class AnnotationWrapper {
|
||||
return;
|
||||
}
|
||||
|
||||
if (redactionLogEntryWrapper.manualRedactionType === 'RESIZE') {
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
annotationWrapper.superType = 'suggestion-resize';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (redactionLogEntryWrapper.manualRedactionType === 'FORCE_REDACT') {
|
||||
annotationWrapper.force = true;
|
||||
|
||||
@ -263,10 +257,6 @@ export class AnnotationWrapper {
|
||||
annotationWrapper.superType = 'suggestion-add-dictionary';
|
||||
return;
|
||||
}
|
||||
if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||
annotationWrapper.superType = 'add-dictionary';
|
||||
return;
|
||||
}
|
||||
if (redactionLogEntryWrapper.status === 'DECLINED') {
|
||||
annotationWrapper.superType = 'declined-suggestion';
|
||||
return;
|
||||
@ -318,10 +308,6 @@ export class AnnotationWrapper {
|
||||
annotationWrapper.superType = 'suggestion-add-dictionary';
|
||||
return;
|
||||
}
|
||||
if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||
annotationWrapper.superType = 'add-dictionary';
|
||||
return;
|
||||
}
|
||||
if (redactionLogEntryWrapper.status === 'DECLINED') {
|
||||
annotationWrapper.superType = 'declined-suggestion';
|
||||
return;
|
||||
@ -369,11 +355,16 @@ export class AnnotationWrapper {
|
||||
|
||||
if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||
if (redactionLogEntryWrapper.dictionaryEntry) {
|
||||
annotationWrapper.superType =
|
||||
redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'add-dictionary' : 'remove-dictionary';
|
||||
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'skipped';
|
||||
} else {
|
||||
annotationWrapper.superType =
|
||||
redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'manual-redaction' : 'remove-only-here';
|
||||
redactionLogEntryWrapper.manualRedactionType === 'ADD'
|
||||
? 'manual-redaction'
|
||||
: annotationWrapper.redaction
|
||||
? 'redaction'
|
||||
: annotationWrapper.hint
|
||||
? 'hint'
|
||||
: 'skipped';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ export interface RedactionLogEntryWrapper {
|
||||
id?: string;
|
||||
legalBasis?: string;
|
||||
manual?: boolean;
|
||||
manualRedactionType?: 'ADD' | 'REMOVE' | 'UNDO' | 'LEGAL_BASIS_CHANGE' | 'FORCE_REDACT' | 'RECATEGORIZE';
|
||||
manualRedactionType?: 'ADD' | 'REMOVE' | 'LEGAL_BASIS_CHANGE' | 'FORCE_REDACT' | 'RECATEGORIZE' | 'RESIZE';
|
||||
matchedRule?: number;
|
||||
positions?: Array<IRectangle>;
|
||||
reason?: string;
|
||||
|
||||
@ -5,7 +5,6 @@ import { HttpClientModule } from '@angular/common/http';
|
||||
import { KeycloakAngularModule, KeycloakOptions, KeycloakService } from 'keycloak-angular';
|
||||
import { ConfigService } from '@services/config.service';
|
||||
import { BASE_HREF } from '../../tokens';
|
||||
import { environment } from '@environments/environment';
|
||||
|
||||
function getKeycloakOptions(configService: ConfigService, baseUrl: string) {
|
||||
let url: string = configService.values.OAUTH_URL;
|
||||
|
||||
@ -1,109 +1,142 @@
|
||||
<div *ngIf="canPerformAnnotationActions" [class.always-visible]="alwaysVisible" class="annotation-actions">
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.changeLegalBasis($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canChangeLegalBasis"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.edit-reason.label' | translate"
|
||||
[type]="buttonType"
|
||||
icon="iqser:edit"
|
||||
></iqser-circle-button>
|
||||
<!-- Resize Mode for annotation -> only resize accept and deny actions are available-->
|
||||
<ng-container *ngIf="resizing">
|
||||
<iqser-circle-button
|
||||
(action)="acceptResize($event)"
|
||||
*ngIf="annotationPermissions.canResizeAnnotation && annotations.length === 1 && annotations[0].resizing"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.resize-accept.label' | translate"
|
||||
[type]="buttonType"
|
||||
icon="iqser:check"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.convertRecommendationToAnnotation($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canAcceptRecommendation"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.accept-recommendation.label' | translate"
|
||||
[type]="buttonType"
|
||||
icon="iqser:check"
|
||||
></iqser-circle-button>
|
||||
<iqser-circle-button
|
||||
(action)="cancelResize($event)"
|
||||
*ngIf="annotationPermissions.canResizeAnnotation && annotations.length === 1 && annotations[0].resizing"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.resize-cancel.label' | translate"
|
||||
[type]="buttonType"
|
||||
icon="iqser:close"
|
||||
></iqser-circle-button>
|
||||
</ng-container>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.acceptSuggestion($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canAcceptSuggestion"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.accept-suggestion.label' | translate"
|
||||
[type]="buttonType"
|
||||
icon="iqser:check"
|
||||
></iqser-circle-button>
|
||||
<!-- Not resizing - standard actions -->
|
||||
<ng-container *ngIf="!resizing">
|
||||
<iqser-circle-button
|
||||
(action)="resize($event)"
|
||||
*ngIf="annotationPermissions.canResizeAnnotation && annotations.length === 1"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.resize.label' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:resize"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.undoDirectAction($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canUndo"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.undo' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:undo"
|
||||
></iqser-circle-button>
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.changeLegalBasis($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canChangeLegalBasis"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.edit-reason.label' | translate"
|
||||
[type]="buttonType"
|
||||
icon="iqser:edit"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.rejectSuggestion($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canRejectSuggestion"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.reject-suggestion' | translate"
|
||||
[type]="buttonType"
|
||||
icon="iqser:close"
|
||||
></iqser-circle-button>
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.convertRecommendationToAnnotation($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canAcceptRecommendation"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.accept-recommendation.label' | translate"
|
||||
[type]="buttonType"
|
||||
icon="iqser:check"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.recategorizeImages($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canRecategorizeImage"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.recategorize-image' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:thumb-down"
|
||||
></iqser-circle-button>
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.acceptSuggestion($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canAcceptSuggestion"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.accept-suggestion.label' | translate"
|
||||
[type]="buttonType"
|
||||
icon="iqser:check"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.forceRedaction($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canForceRedaction"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.force-redaction.label' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:thumb-up"
|
||||
></iqser-circle-button>
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.undoDirectAction($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canUndo"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.undo' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:undo"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="hideAnnotation($event)"
|
||||
*ngIf="isImage && isVisible"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.hide' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:visibility-off"
|
||||
></iqser-circle-button>
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.rejectSuggestion($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canRejectSuggestion"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.reject-suggestion' | translate"
|
||||
[type]="buttonType"
|
||||
icon="iqser:close"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="showAnnotation($event)"
|
||||
*ngIf="isImage && !isVisible"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.show' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:visibility"
|
||||
></iqser-circle-button>
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.recategorizeImages($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canRecategorizeImage"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.recategorize-image' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:thumb-down"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="suggestRemoveAnnotations($event, true)"
|
||||
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveFromDictionary"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.remove-annotation.remove-from-dict' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:remove-from-dict"
|
||||
></iqser-circle-button>
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.forceRedaction($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canForceRedaction"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.force-redaction.label' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:thumb-up"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="markAsFalsePositive($event)"
|
||||
*ngIf="annotationPermissions.canMarkAsFalsePositive"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.remove-annotation.false-positive' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:thumb-down"
|
||||
></iqser-circle-button>
|
||||
<iqser-circle-button
|
||||
(action)="hideAnnotation($event)"
|
||||
*ngIf="isImage && isVisible"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.hide' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:visibility-off"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="suggestRemoveAnnotations($event, false)"
|
||||
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveOnlyHere"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.remove-annotation.only-here' | translate"
|
||||
[type]="buttonType"
|
||||
icon="iqser:trash"
|
||||
></iqser-circle-button>
|
||||
<iqser-circle-button
|
||||
(action)="showAnnotation($event)"
|
||||
*ngIf="isImage && !isVisible"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.show' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:visibility"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="suggestRemoveAnnotations($event, true)"
|
||||
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveFromDictionary"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.remove-annotation.remove-from-dict' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:remove-from-dict"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="markAsFalsePositive($event)"
|
||||
*ngIf="annotationPermissions.canMarkAsFalsePositive"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.remove-annotation.false-positive' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:thumb-down"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="suggestRemoveAnnotations($event, false)"
|
||||
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveOnlyHere"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.remove-annotation.only-here' | translate"
|
||||
[type]="buttonType"
|
||||
icon="iqser:trash"
|
||||
></iqser-circle-button>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
@ -63,6 +63,10 @@ export class AnnotationActionsComponent implements OnInit {
|
||||
return this.annotations?.reduce((accumulator, annotation) => annotation.isImage && accumulator, true);
|
||||
}
|
||||
|
||||
get resizing() {
|
||||
return this.annotations?.length === 1 && this.annotations?.[0].resizing;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._setPermissions();
|
||||
}
|
||||
@ -97,4 +101,16 @@ export class AnnotationActionsComponent implements OnInit {
|
||||
this.annotations,
|
||||
);
|
||||
}
|
||||
|
||||
resize($event: MouseEvent) {
|
||||
this.annotationActionsService.resize($event, this.viewer, this.annotations[0]);
|
||||
}
|
||||
|
||||
acceptResize($event: MouseEvent) {
|
||||
this.annotationActionsService.acceptResize($event, this.viewer, this.annotations[0], this.annotationsChanged);
|
||||
}
|
||||
|
||||
cancelResize($event: MouseEvent) {
|
||||
this.annotationActionsService.cancelResize($event, this.viewer, this.annotations[0], this.annotationsChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,8 +40,9 @@ export class PageExclusionComponent implements OnChanges {
|
||||
}, []);
|
||||
}
|
||||
|
||||
async excludePagesRange(value: string): Promise<void> {
|
||||
async excludePagesRange(inputValue: string): Promise<void> {
|
||||
this._loadingService.start();
|
||||
const value = inputValue.replace(/[^0-9-,]/g, '');
|
||||
try {
|
||||
const pageRanges = value.split(',').map(range => {
|
||||
const splitted = range.split('-');
|
||||
|
||||
@ -36,6 +36,7 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import Tools = Core.Tools;
|
||||
import TextTool = Tools.TextTool;
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
import { toPosition } from '../../utils/pdf-calculation.utils';
|
||||
|
||||
const ALLOWED_KEYBOARD_SHORTCUTS = ['+', '-', 'p', 'r', 'Escape'] as const;
|
||||
const dataElements = {
|
||||
@ -253,6 +254,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
|
||||
// this will auto select rectangle after drawing
|
||||
if (annotations.length === 1 && annotations[0].ToolName === 'AnnotationCreateRectangle') {
|
||||
this.annotationManager.selectAnnotations(annotations);
|
||||
annotations[0].setRotationControlEnabled(false);
|
||||
}
|
||||
});
|
||||
|
||||
@ -456,7 +458,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
this.instance.UI.annotationPopup.add(
|
||||
this._annotationActionsService.getViewerAvailableActions(annotationWrappers, this.annotationsChanged),
|
||||
this._annotationActionsService.getViewerAvailableActions(this.instance, annotationWrappers, this.annotationsChanged),
|
||||
);
|
||||
}
|
||||
|
||||
@ -598,7 +600,8 @@ export class PdfViewerComponent implements OnInit, OnChanges {
|
||||
for (const key of Object.keys(quads)) {
|
||||
for (const quad of quads[key]) {
|
||||
const page = parseInt(key, 10);
|
||||
entry.positions.push(this.utils.toPosition(page, convertQuads ? this.utils.translateQuads(page, quad) : quad));
|
||||
const pageHeight = this.documentViewer.getPageHeight(page);
|
||||
entry.positions.push(toPosition(page, pageHeight, convertQuads ? this.utils.translateQuads(page, quad) : quad));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -36,8 +36,6 @@ export class TypeAnnotationIconComponent implements OnChanges {
|
||||
? 'S'
|
||||
: this.annotation.isSkipped
|
||||
? 'S'
|
||||
: this.annotation.isReadyForAnalysis
|
||||
? 'A'
|
||||
: this.annotation.type[0].toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
<section class="dialog">
|
||||
<form (submit)="save()" [formGroup]="resizeForm">
|
||||
<div class="dialog-header heading-l" translate="resize-annotation-dialog.header"></div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<div [class.required]="!isDocumentAdmin" class="iqser-input-group w-300">
|
||||
<label translate="resize-annotation-dialog.content.comment"></label>
|
||||
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button [disabled]="!resizeForm.valid" color="primary" mat-flat-button type="submit">
|
||||
{{ 'resize-annotation-dialog.actions.save' | translate }}
|
||||
</button>
|
||||
<div class="all-caps-label cancel" mat-dialog-close translate="resize-annotation-dialog.actions.cancel"></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
||||
</section>
|
||||
@ -0,0 +1,34 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-resize-annotation-dialog',
|
||||
templateUrl: './resize-annotation-dialog.component.html',
|
||||
styleUrls: ['./resize-annotation-dialog.component.scss'],
|
||||
})
|
||||
export class ResizeAnnotationDialogComponent implements OnInit {
|
||||
resizeForm: FormGroup;
|
||||
isDocumentAdmin: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
public dialogRef: MatDialogRef<ResizeAnnotationDialogComponent>,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.isDocumentAdmin = this._permissionsService.isApprover();
|
||||
|
||||
this.resizeForm = this._formBuilder.group({
|
||||
comment: this.isDocumentAdmin ? [null] : [null, Validators.required],
|
||||
});
|
||||
}
|
||||
|
||||
save() {
|
||||
this.dialogRef.close({
|
||||
comment: this.resizeForm.get('comment').value,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -40,6 +40,7 @@ import { AnnotationSourceComponent } from './components/file-workload/components
|
||||
import { OverlayModule } from '@angular/cdk/overlay';
|
||||
import { SharedDossiersModule } from './shared/shared-dossiers.module';
|
||||
import { PlatformSearchService } from './shared/services/platform-search.service';
|
||||
import { ResizeAnnotationDialogComponent } from './dialogs/resize-annotation-dialog/resize-annotation-dialog.component';
|
||||
|
||||
const screens = [FilePreviewScreenComponent, SearchScreenComponent];
|
||||
|
||||
@ -49,6 +50,7 @@ const dialogs = [
|
||||
ManualAnnotationDialogComponent,
|
||||
ForceRedactionDialogComponent,
|
||||
RemoveAnnotationsDialogComponent,
|
||||
ResizeAnnotationDialogComponent,
|
||||
DocumentInfoDialogComponent,
|
||||
AssignReviewerApproverDialogComponent,
|
||||
ChangeLegalBasisDialogComponent,
|
||||
|
||||
@ -11,4 +11,6 @@ export type AnnotationActionMode =
|
||||
| 'suggest'
|
||||
| 'undo'
|
||||
| 'force-redaction'
|
||||
| 'request-force-redaction';
|
||||
| 'request-force-redaction'
|
||||
| 'resize'
|
||||
| 'request-resize';
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { StatusSorter } from '../../../../../../../../../../libs/red-domain/src/lib/shared/sorters/status-sorter';
|
||||
import { CircleButtonTypes, StatusBarConfig } from '@iqser/common-ui';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { Dossier } from '@red/domain';
|
||||
import { Dossier, StatusSorter } from '@red/domain';
|
||||
import { DossiersDialogService } from '../../../../services/dossiers-dialog.service';
|
||||
import { LongPressEvent } from '@shared/directives/long-press.directive';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
|
||||
@ -9,8 +9,11 @@ import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
||||
import { DossiersDialogService } from './dossiers-dialog.service';
|
||||
import { BASE_HREF } from '../../../tokens';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { Core } from '@pdftron/webviewer';
|
||||
import { IAddRedactionRequest, ILegalBasisChangeRequest } from '@red/domain';
|
||||
import { Core, WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { IAddRedactionRequest, ILegalBasisChangeRequest, IRectangle, IResizeRequest } from '@red/domain';
|
||||
import { AppStateService } from '../../../state/app-state.service';
|
||||
import { toPosition } from '../utils/pdf-calculation.utils';
|
||||
import { AnnotationDrawService } from './annotation-draw.service';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
|
||||
@Injectable()
|
||||
@ -18,11 +21,13 @@ export class AnnotationActionsService {
|
||||
constructor(
|
||||
@Inject(BASE_HREF) private readonly _baseHref: string,
|
||||
private readonly _ngZone: NgZone,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
) {}
|
||||
|
||||
acceptSuggestion($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
||||
@ -123,6 +128,7 @@ export class AnnotationActionsService {
|
||||
}
|
||||
|
||||
getViewerAvailableActions(
|
||||
viewer: WebViewerInstance,
|
||||
annotations: AnnotationWrapper[],
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
): Record<string, unknown>[] {
|
||||
@ -133,6 +139,47 @@ export class AnnotationActionsService {
|
||||
permissions: AnnotationPermissions.forUser(this._permissionsService.isApprover(), this._userService.currentUser, annotation),
|
||||
}));
|
||||
|
||||
// you can only resize one annotation at a time
|
||||
const canResize = annotationPermissions.length === 1 && annotationPermissions[0].permissions.canResizeAnnotation;
|
||||
if (canResize) {
|
||||
const firstAnnotation = annotations[0];
|
||||
// if we already entered resize-mode previously
|
||||
if (firstAnnotation.resizing) {
|
||||
availableActions.push({
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/check.svg'),
|
||||
title: this._translateService.instant('annotation-actions.resize-accept.label'),
|
||||
onClick: () => {
|
||||
this._ngZone.run(() => {
|
||||
this.acceptResize(null, viewer, firstAnnotation, annotationsChanged);
|
||||
});
|
||||
},
|
||||
});
|
||||
availableActions.push({
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/close.svg'),
|
||||
title: this._translateService.instant('annotation-actions.resize-cancel.label'),
|
||||
onClick: () => {
|
||||
this._ngZone.run(() => {
|
||||
this.cancelResize(null, viewer, firstAnnotation, annotationsChanged);
|
||||
});
|
||||
},
|
||||
});
|
||||
return availableActions;
|
||||
}
|
||||
|
||||
availableActions.push({
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/resize.svg'),
|
||||
title: this._translateService.instant('annotation-actions.resize.label'),
|
||||
onClick: () => {
|
||||
this._ngZone.run(() => {
|
||||
this.resize(null, viewer, annotations[0]);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const canChangeLegalBasis = annotationPermissions.reduce((acc, next) => acc && next.permissions.canChangeLegalBasis, true);
|
||||
if (canChangeLegalBasis) {
|
||||
availableActions.push({
|
||||
@ -337,4 +384,116 @@ export class AnnotationActionsService {
|
||||
private _convertPath(path: string): string {
|
||||
return this._baseHref + path;
|
||||
}
|
||||
|
||||
resize($event: MouseEvent, viewer: WebViewerInstance, annotationWrapper: AnnotationWrapper) {
|
||||
$event?.stopPropagation();
|
||||
|
||||
annotationWrapper.resizing = true;
|
||||
|
||||
const annotationManager = viewer.Core.annotationManager;
|
||||
const viewerAnnotation = annotationManager.getAnnotationById(annotationWrapper.id);
|
||||
viewerAnnotation.ReadOnly = false;
|
||||
viewerAnnotation.setRotationControlEnabled(false);
|
||||
annotationManager.redrawAnnotation(viewerAnnotation);
|
||||
annotationManager.selectAnnotation(viewerAnnotation);
|
||||
}
|
||||
|
||||
private async _extractTextAndPositions(viewer: WebViewerInstance, annotationId: string) {
|
||||
const viewerAnnotation = viewer.Core.annotationManager.getAnnotationById(annotationId);
|
||||
|
||||
const document = await viewer.Core.documentViewer.getDocument().getPDFDoc();
|
||||
const page = await document.getPage(viewerAnnotation.getPageNumber());
|
||||
if (viewerAnnotation instanceof viewer.Core.Annotations.TextHighlightAnnotation) {
|
||||
const words = [];
|
||||
const rectangles: IRectangle[] = [];
|
||||
for (const quad of viewerAnnotation.Quads) {
|
||||
const rect = toPosition(viewerAnnotation.getPageNumber(), await page.getPageHeight(), quad);
|
||||
rectangles.push(rect);
|
||||
const pdfNetRect = new viewer.Core.PDFNet.Rect(
|
||||
rect.topLeft.x,
|
||||
rect.topLeft.y,
|
||||
rect.topLeft.x + rect.width,
|
||||
rect.topLeft.y + rect.height,
|
||||
);
|
||||
const quadWords = await this._extractTextFromRect(viewer, page, pdfNetRect);
|
||||
words.push(...quadWords);
|
||||
}
|
||||
|
||||
return {
|
||||
text: words.join(' '),
|
||||
positions: rectangles,
|
||||
};
|
||||
} else {
|
||||
const rect = toPosition(
|
||||
viewerAnnotation.getPageNumber(),
|
||||
await page.getPageHeight(),
|
||||
this._annotationDrawService.annotationToQuads(viewerAnnotation, viewer),
|
||||
);
|
||||
return {
|
||||
positions: [rect],
|
||||
text: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async _extractTextFromRect(viewer: WebViewerInstance, page: Core.PDFNet.Page, rect: Core.PDFNet.Rect) {
|
||||
const txt = await viewer.Core.PDFNet.TextExtractor.create();
|
||||
txt.begin(page, rect); // Read the page.
|
||||
|
||||
const words = [];
|
||||
// Extract words one by one.
|
||||
let line = await txt.getFirstLine();
|
||||
for (; await line.isValid(); line = await line.getNextLine()) {
|
||||
for (let word = await line.getFirstWord(); await word.isValid(); word = await word.getNextWord()) {
|
||||
words.push(await word.getString());
|
||||
}
|
||||
}
|
||||
return words;
|
||||
}
|
||||
|
||||
acceptResize(
|
||||
$event: MouseEvent,
|
||||
viewer: WebViewerInstance,
|
||||
annotationWrapper: AnnotationWrapper,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
this._dialogService.openDialog('resizeAnnotation', $event, null, async (result: { comment: string }) => {
|
||||
const textAndPositions = await this._extractTextAndPositions(viewer, annotationWrapper.id);
|
||||
const text =
|
||||
annotationWrapper.value === 'Rectangle' ? 'Rectangle' : annotationWrapper.isImage ? 'Image' : textAndPositions.text;
|
||||
|
||||
const resizeRequest: IResizeRequest = {
|
||||
annotationId: annotationWrapper.id,
|
||||
comment: result.comment,
|
||||
positions: textAndPositions.positions,
|
||||
value: text,
|
||||
};
|
||||
|
||||
console.log(resizeRequest);
|
||||
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.resizeOrSuggestToResize(annotationWrapper, resizeRequest),
|
||||
annotationWrapper,
|
||||
annotationsChanged,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
cancelResize(
|
||||
$event: MouseEvent,
|
||||
viewer: WebViewerInstance,
|
||||
annotationWrapper: AnnotationWrapper,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
$event?.stopPropagation();
|
||||
|
||||
annotationWrapper.resizing = false;
|
||||
|
||||
const annotationManager = viewer.Core.annotationManager;
|
||||
const viewerAnnotation = annotationManager.getAnnotationById(annotationWrapper.id);
|
||||
viewerAnnotation.ReadOnly = false;
|
||||
annotationManager.redrawAnnotation(viewerAnnotation);
|
||||
annotationManager.deselectAllAnnotations();
|
||||
annotationsChanged.emit(annotationWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,32 +141,50 @@ export class AnnotationDrawService {
|
||||
compareMode = false,
|
||||
) {
|
||||
const pageNumber = compareMode ? annotationWrapper.pageNumber * 2 - 1 : annotationWrapper.pageNumber;
|
||||
const highlight = new activeViewer.Core.Annotations.TextHighlightAnnotation();
|
||||
highlight.PageNumber = pageNumber;
|
||||
highlight.StrokeColor = this.getColor(activeViewer, annotationWrapper.superType, annotationWrapper.type);
|
||||
highlight.setContents(annotationWrapper.content);
|
||||
highlight.Quads = this._rectanglesToQuads(annotationWrapper.positions, activeViewer, pageNumber);
|
||||
highlight.Id = annotationWrapper.id;
|
||||
highlight.ReadOnly = true;
|
||||
|
||||
let annotation;
|
||||
if (annotationWrapper.value === 'Rectangle' || annotationWrapper.isImage) {
|
||||
annotation = new activeViewer.Core.Annotations.RectangleAnnotation();
|
||||
const pageHeight = activeViewer.Core.documentViewer.getPageHeight(pageNumber);
|
||||
const firstPosition = annotationWrapper.positions[0];
|
||||
annotation.X = firstPosition.topLeft.x;
|
||||
annotation.Y = pageHeight - (firstPosition.topLeft.y + firstPosition.height);
|
||||
annotation.Width = firstPosition.width;
|
||||
annotation.FillColor = this.getColor(activeViewer, annotationWrapper.superType, annotationWrapper.type);
|
||||
annotation.Opacity = annotationWrapper.isChangeLogRemoved ? 0.2 : 0.6;
|
||||
annotation.Height = firstPosition.height;
|
||||
annotation.Intensity = 100;
|
||||
} else {
|
||||
annotation = new activeViewer.Core.Annotations.TextHighlightAnnotation();
|
||||
annotation.Quads = this._rectanglesToQuads(annotationWrapper.positions, activeViewer, pageNumber);
|
||||
annotation.Opacity = annotationWrapper.isChangeLogRemoved ? 0.2 : 1;
|
||||
}
|
||||
|
||||
annotation.setContents(annotationWrapper.content);
|
||||
|
||||
annotation.PageNumber = pageNumber;
|
||||
annotation.StrokeColor = this.getColor(activeViewer, annotationWrapper.superType, annotationWrapper.type);
|
||||
annotation.Id = annotationWrapper.id;
|
||||
annotation.ReadOnly = true;
|
||||
// change log entries are drawn lighter
|
||||
highlight.Opacity = annotationWrapper.isChangeLogRemoved ? 0.2 : 1;
|
||||
highlight.Hidden =
|
||||
|
||||
annotation.Hidden =
|
||||
annotationWrapper.isChangeLogRemoved ||
|
||||
(hideSkipped && annotationWrapper.isSkipped) ||
|
||||
annotationWrapper.isOCR ||
|
||||
annotationWrapper.hidden;
|
||||
highlight.setCustomData('redacto-manager', 'true');
|
||||
highlight.setCustomData('redaction', String(annotationWrapper.isRedacted));
|
||||
highlight.setCustomData('skipped', String(annotationWrapper.isSkipped));
|
||||
highlight.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry));
|
||||
highlight.setCustomData('changeLogRemoved', String(annotationWrapper.isChangeLogRemoved));
|
||||
highlight.setCustomData('redactionColor', String(this.getColor(activeViewer, 'redaction', 'redaction')));
|
||||
highlight.setCustomData(
|
||||
annotation.setCustomData('redacto-manager', 'true');
|
||||
annotation.setCustomData('redaction', String(annotationWrapper.isRedacted));
|
||||
annotation.setCustomData('skipped', String(annotationWrapper.isSkipped));
|
||||
annotation.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry));
|
||||
annotation.setCustomData('changeLogRemoved', String(annotationWrapper.isChangeLogRemoved));
|
||||
annotation.setCustomData('redactionColor', String(this.getColor(activeViewer, 'redaction', 'redaction')));
|
||||
annotation.setCustomData(
|
||||
'annotationColor',
|
||||
String(this.getColor(activeViewer, annotationWrapper.superType, annotationWrapper.type)),
|
||||
);
|
||||
|
||||
return highlight;
|
||||
return annotation;
|
||||
}
|
||||
|
||||
private _rectanglesToQuads(positions: IRectangle[], activeViewer: WebViewerInstance, pageNumber: number): any[] {
|
||||
|
||||
@ -10,6 +10,7 @@ import { AssignReviewerApproverDialogComponent } from '../dialogs/assign-reviewe
|
||||
import { ChangeLegalBasisDialogComponent } from '../dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component';
|
||||
import { RecategorizeImageDialogComponent } from '../dialogs/recategorize-image-dialog/recategorize-image-dialog.component';
|
||||
import { ConfirmationDialogComponent, DialogConfig, DialogService, largeDialogConfig } from '@iqser/common-ui';
|
||||
import { ResizeAnnotationDialogComponent } from '../dialogs/resize-annotation-dialog/resize-annotation-dialog.component';
|
||||
|
||||
type DialogType =
|
||||
| 'confirm'
|
||||
@ -20,6 +21,7 @@ type DialogType =
|
||||
| 'recategorizeImage'
|
||||
| 'changeLegalBasis'
|
||||
| 'removeAnnotations'
|
||||
| 'resizeAnnotation'
|
||||
| 'forceRedaction'
|
||||
| 'manualAnnotation';
|
||||
|
||||
@ -53,6 +55,9 @@ export class DossiersDialogService extends DialogService<DialogType> {
|
||||
removeAnnotations: {
|
||||
component: RemoveAnnotationsDialogComponent,
|
||||
},
|
||||
resizeAnnotation: {
|
||||
component: ResizeAnnotationDialogComponent,
|
||||
},
|
||||
forceRedaction: {
|
||||
component: ForceRedactionDialogComponent,
|
||||
},
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
ILegalBasisChangeRequest,
|
||||
IManualAddResponse,
|
||||
IRemoveRedactionRequest,
|
||||
IResizeRequest,
|
||||
} from '@red/domain';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { CONFLICT_ERROR_CODE, ErrorMessageService, GenericService, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
|
||||
@ -51,6 +52,8 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
suggest: 'requestAddRedaction',
|
||||
'force-redaction': 'forceRedaction',
|
||||
'request-force-redaction': 'requestForceRedaction',
|
||||
resize: 'resize',
|
||||
'request-resize': 'requestResize',
|
||||
};
|
||||
}
|
||||
|
||||
@ -162,6 +165,14 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
return this._makeRequest(mode, annotationWrapper.id, null, annotationWrapper.isModifyDictionary);
|
||||
}
|
||||
|
||||
// this wraps
|
||||
// /manualRedaction/redaction/resize/
|
||||
// /manualRedaction/request/resize/
|
||||
resizeOrSuggestToResize(annotationWrapper: AnnotationWrapper, resizeRequest: IResizeRequest) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover() ? 'resize' : 'request-resize';
|
||||
return this._makeRequest(mode, resizeRequest);
|
||||
}
|
||||
|
||||
// this wraps
|
||||
// /manualRedaction/redaction/remove/
|
||||
// /manualRedaction/request/remove/
|
||||
@ -322,6 +333,18 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
return this._post(body, url);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
resize(@RequiredParam() body: IResizeRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
|
||||
const url = `${this._defaultModelPath}/redaction/resize/${dossierId}/${fileId}`;
|
||||
return this._post(body, url);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
requestResize(@RequiredParam() body: IResizeRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
|
||||
const url = `${this._defaultModelPath}/request/resize/${dossierId}/${fileId}`;
|
||||
return this._post(body, url);
|
||||
}
|
||||
|
||||
private _getMessage(mode: AnnotationActionMode, modifyDictionary?: boolean, error = false, isConflict = false) {
|
||||
const type = modifyDictionary ? 'dictionary' : 'manual-redaction';
|
||||
const resultType = error ? (isConflict ? 'conflictError' : 'error') : 'success';
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
import { IRectangle } from '@red/domain';
|
||||
|
||||
export const toPosition = (
|
||||
page: number,
|
||||
pageHeight: number,
|
||||
selectedQuad: { x1: number; x2: number; x3: number; x4: number; y4: number; y2: number },
|
||||
): IRectangle => {
|
||||
const height = selectedQuad.y2 - selectedQuad.y4;
|
||||
return {
|
||||
page: page,
|
||||
topLeft: {
|
||||
x: Math.min(selectedQuad.x3, selectedQuad.x4, selectedQuad.x2, selectedQuad.x1),
|
||||
y: pageHeight - (selectedQuad.y4 + height),
|
||||
},
|
||||
height: height,
|
||||
width: Math.max(4, Math.abs(selectedQuad.x3 - selectedQuad.x4), Math.abs(selectedQuad.x3 - selectedQuad.x1)),
|
||||
};
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
import { IRectangle, ViewMode } from '@red/domain';
|
||||
import { ViewMode } from '@red/domain';
|
||||
import { translateQuads } from '@utils/pdf-coordinates';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { Core, WebViewerInstance } from '@pdftron/webviewer';
|
||||
@ -120,20 +120,6 @@ export class PdfViewerUtils {
|
||||
return translateQuads(page, rotation, quads);
|
||||
}
|
||||
|
||||
toPosition(page: number, selectedQuad: { x1: number; x2: number; x3: number; x4: number; y4: number; y2: number }): IRectangle {
|
||||
const pageHeight = this._documentViewer.getPageHeight(page);
|
||||
const height = selectedQuad.y2 - selectedQuad.y4;
|
||||
return {
|
||||
page: page,
|
||||
topLeft: {
|
||||
x: Math.min(selectedQuad.x3, selectedQuad.x4, selectedQuad.x2, selectedQuad.x1),
|
||||
y: pageHeight - (selectedQuad.y4 + height),
|
||||
},
|
||||
height: height,
|
||||
width: Math.max(4, Math.abs(selectedQuad.x3 - selectedQuad.x4), Math.abs(selectedQuad.x3 - selectedQuad.x1)),
|
||||
};
|
||||
}
|
||||
|
||||
deselectAllAnnotations() {
|
||||
this._annotationManager.deselectAllAnnotations();
|
||||
}
|
||||
|
||||
@ -52,6 +52,7 @@ export class IconsModule {
|
||||
'reason',
|
||||
'remove-from-dict',
|
||||
'report',
|
||||
'resize',
|
||||
'rule',
|
||||
'secret',
|
||||
'status',
|
||||
|
||||
@ -20,14 +20,7 @@ export class TypeFilterComponent implements OnInit {
|
||||
'suggestion-remove-dictionary',
|
||||
'suggestion-add-dictionary',
|
||||
];
|
||||
private _needsAnalysisKeys = [
|
||||
'add-dictionary',
|
||||
'remove-dictionary',
|
||||
'remove-only-here',
|
||||
'pending-analysis',
|
||||
'change-legal-basis',
|
||||
'analysis',
|
||||
];
|
||||
private _needsAnalysisKeys = ['add-dictionary', 'remove-only-here', 'change-legal-basis', 'analysis'];
|
||||
|
||||
constructor(private readonly _appStateService: AppStateService) {}
|
||||
|
||||
|
||||
@ -338,21 +338,7 @@ export class AppStateService {
|
||||
},
|
||||
true,
|
||||
);
|
||||
// dictionary actions
|
||||
dictionaryData['add-dictionary'] = new Dictionary(
|
||||
{
|
||||
hexColor: colors.analysisColor || FALLBACK_COLOR,
|
||||
type: 'add-dictionary',
|
||||
},
|
||||
true,
|
||||
);
|
||||
dictionaryData['remove-dictionary'] = new Dictionary(
|
||||
{
|
||||
hexColor: colors.analysisColor || FALLBACK_COLOR,
|
||||
type: 'remove-dictionary',
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
dictionaryData['remove-only-here'] = new Dictionary(
|
||||
{
|
||||
hexColor: colors.analysisColor || FALLBACK_COLOR,
|
||||
@ -376,6 +362,7 @@ export class AppStateService {
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
// add suggestions
|
||||
dictionaryData['suggestion-change-legal-basis'] = new Dictionary(
|
||||
{
|
||||
@ -384,6 +371,7 @@ export class AppStateService {
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
dictionaryData['suggestion-recategorize-image'] = new Dictionary(
|
||||
{
|
||||
hexColor: colors.requestAdd || FALLBACK_COLOR,
|
||||
@ -391,6 +379,7 @@ export class AppStateService {
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
dictionaryData['suggestion-add-dictionary'] = new Dictionary(
|
||||
{
|
||||
hexColor: colors.dictionaryRequestColor || FALLBACK_COLOR,
|
||||
@ -399,6 +388,14 @@ export class AppStateService {
|
||||
true,
|
||||
);
|
||||
|
||||
dictionaryData['suggestion-resize'] = new Dictionary(
|
||||
{
|
||||
hexColor: colors.dictionaryRequestColor || FALLBACK_COLOR,
|
||||
type: 'suggestion-resize',
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
dictionaryData['suggestion-remove'] = new Dictionary(
|
||||
{
|
||||
hexColor: colors.requestRemove || FALLBACK_COLOR,
|
||||
@ -447,22 +444,6 @@ export class AppStateService {
|
||||
true,
|
||||
);
|
||||
|
||||
dictionaryData['pending-analysis'] = new Dictionary(
|
||||
{
|
||||
hexColor: colors.analysisColor || FALLBACK_COLOR,
|
||||
type: 'analysis',
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
dictionaryData['change-legal-basis'] = new Dictionary(
|
||||
{
|
||||
hexColor: colors.analysisColor || FALLBACK_COLOR,
|
||||
type: 'analysis',
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
dictionaryData['hint'] = new Dictionary(
|
||||
{
|
||||
hexColor: '#fa98f7',
|
||||
|
||||
@ -2,16 +2,11 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { AnnotationSuperType } from '../models/file/annotation.wrapper';
|
||||
|
||||
export const annotationTypesTranslations: { [key in AnnotationSuperType]: string } = {
|
||||
'add-dictionary': _('annotation-type.add-dictionary'),
|
||||
'change-legal-basis': _('annotation-type.change-legal-basis'),
|
||||
'declined-suggestion': _('annotation-type.declined-suggestion'),
|
||||
hint: _('annotation-type.hint'),
|
||||
'manual-redaction': _('annotation-type.manual-redaction'),
|
||||
'pending-analysis': _('annotation-type.pending-analysis'),
|
||||
recommendation: _('annotation-type.recommendation'),
|
||||
redaction: _('annotation-type.redaction'),
|
||||
'remove-dictionary': _('annotation-type.remove-dictionary'),
|
||||
'remove-only-here': _('annotation-type.remove-only-here'),
|
||||
skipped: _('annotation-type.skipped'),
|
||||
'suggestion-add': _('annotation-type.suggestion-add'),
|
||||
'suggestion-add-dictionary': _('annotation-type.suggestion-add-dictionary'),
|
||||
@ -20,4 +15,5 @@ export const annotationTypesTranslations: { [key in AnnotationSuperType]: string
|
||||
'suggestion-force-redaction': _('annotation-type.suggestion-force-redaction'),
|
||||
'suggestion-remove': _('annotation-type.suggestion-remove'),
|
||||
'suggestion-remove-dictionary': _('annotation-type.suggestion-remove-dictionary'),
|
||||
'suggestion-resize': _('annotation-type.suggestion-resize'),
|
||||
} as const;
|
||||
|
||||
@ -4,12 +4,8 @@ export const SuperTypeSorter: { [key in AnnotationSuperType]: number } = {
|
||||
'suggestion-change-legal-basis': 14,
|
||||
'suggestion-force-redaction': 15,
|
||||
'suggestion-recategorize-image': 16,
|
||||
'suggestion-resize': 18,
|
||||
recommendation: 7,
|
||||
'add-dictionary': 2,
|
||||
'remove-dictionary': 3,
|
||||
'pending-analysis': 4,
|
||||
'change-legal-basis': 5,
|
||||
'remove-only-here': 6,
|
||||
'suggestion-add-dictionary': 12,
|
||||
'suggestion-remove-dictionary': 13,
|
||||
'suggestion-add': 10,
|
||||
|
||||
@ -138,6 +138,15 @@
|
||||
},
|
||||
"annotation": "Anmerkung",
|
||||
"annotation-actions": {
|
||||
"resize": {
|
||||
"label": "Größe ändern"
|
||||
},
|
||||
"resize-accept": {
|
||||
"label": "Größe speichern"
|
||||
},
|
||||
"resize-cancel": {
|
||||
"label": "Größenänderung abbrechen"
|
||||
},
|
||||
"accept-recommendation": {
|
||||
"label": "Empfehlung annehmen"
|
||||
},
|
||||
@ -264,15 +273,12 @@
|
||||
"rule": "Schwärzung basierend auf Regel {rule}"
|
||||
},
|
||||
"annotation-type": {
|
||||
"add-dictionary": "Ausstehende Ergänzung zum Wörterbuch",
|
||||
"change-legal-basis": "Änderung der Rechtsgrundlage ausstehend",
|
||||
"declined-suggestion": "Abgelehnter Vorschlag",
|
||||
"hint": "Hinweis",
|
||||
"manual-redaction": "Manuelle Schwärzung",
|
||||
"pending-analysis": "Reanalyse ausstehend",
|
||||
"recommendation": "Empfehlung",
|
||||
"redaction": "Schwärzung",
|
||||
"remove-dictionary": "Ausstehende Löschung aus dem Wörterbuch",
|
||||
"remove-only-here": "Ausstehende Löschung (nur hier)",
|
||||
"skipped": "Übersprungen",
|
||||
"suggestion-add": "Vorschlag für Schwärzung",
|
||||
@ -356,6 +362,16 @@
|
||||
"logout": "Ausloggen"
|
||||
},
|
||||
"by": "von",
|
||||
"resize-annotation-dialog": {
|
||||
"actions": {
|
||||
"cancel": "Abbrechen",
|
||||
"save": "Änderungen speichern"
|
||||
},
|
||||
"content": {
|
||||
"comment": "Kommentar"
|
||||
},
|
||||
"header": "Schwärzung ändern"
|
||||
},
|
||||
"change-legal-basis-dialog": {
|
||||
"actions": {
|
||||
"cancel": "Abbrechen",
|
||||
@ -1773,7 +1789,6 @@
|
||||
"redactions": "Schwärzungs-Wörterbücher"
|
||||
},
|
||||
"OCR_PROCESSING": "OCR-Analyse",
|
||||
"pending-analysis": "Reanalyse ausstehend",
|
||||
"PROCESSING": "Wird analysiert",
|
||||
"report": {
|
||||
"action": {
|
||||
|
||||
@ -139,6 +139,15 @@
|
||||
},
|
||||
"annotation": "Annotation",
|
||||
"annotation-actions": {
|
||||
"resize": {
|
||||
"label": "Resize"
|
||||
},
|
||||
"resize-accept": {
|
||||
"label": "Save Resize"
|
||||
},
|
||||
"resize-cancel": {
|
||||
"label": "Abort Resize"
|
||||
},
|
||||
"accept-recommendation": {
|
||||
"label": "Accept Recommendation"
|
||||
},
|
||||
@ -255,15 +264,12 @@
|
||||
"rule": "Redaction based on rule {rule}"
|
||||
},
|
||||
"annotation-type": {
|
||||
"add-dictionary": "Pending add to dictionary",
|
||||
"change-legal-basis": "Pending Change of Legal Basis",
|
||||
"suggestion-resize": "Suggested Resize",
|
||||
"declined-suggestion": "Declined Suggestion",
|
||||
"hint": "Hint",
|
||||
"manual-redaction": "Manual Redaction",
|
||||
"pending-analysis": "Pending Re-Analysis",
|
||||
"recommendation": "Recommendation",
|
||||
"redaction": "Redaction",
|
||||
"remove-dictionary": "Pending remove from dictionary",
|
||||
"remove-only-here": "Pending removal ( only here )",
|
||||
"skipped": "Skipped",
|
||||
"suggestion-add": "Suggested redaction",
|
||||
@ -342,6 +348,16 @@
|
||||
"logout": "Logout"
|
||||
},
|
||||
"by": "by",
|
||||
"resize-annotation-dialog": {
|
||||
"actions": {
|
||||
"cancel": "Cancel",
|
||||
"save": "Save Changes"
|
||||
},
|
||||
"content": {
|
||||
"comment": "Comment"
|
||||
},
|
||||
"header": "Resize Redaction"
|
||||
},
|
||||
"change-legal-basis-dialog": {
|
||||
"actions": {
|
||||
"cancel": "Cancel",
|
||||
|
||||
27
apps/red-ui/src/assets/icons/general/resize.svg
Normal file
27
apps/red-ui/src/assets/icons/general/resize.svg
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>status</title>
|
||||
<g id="Styleguide" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Styleguide-Actions" transform="translate(-979.000000, -630.000000)" fill="currentColor">
|
||||
<g id="reference" transform="translate(969.000000, 620.000000)">
|
||||
<g id="status" transform="translate(10.000000, 10.000000)">
|
||||
<path
|
||||
d="M1.4,9.8 L1.4,12.6 L4.2,12.6 L4.2,14 L0,14 L0,9.8 L1.4,9.8 Z M14,9.8 L14,14 L9.8,14 L9.8,12.6 L12.6,12.6 L12.6,9.8 L14,9.8 Z M4.2,0 L4.2,1.4 L1.4,1.4 L1.4,4.2 L0,4.2 L0,0 L4.2,0 Z M14,0 L14,4.2 L12.6,4.2 L12.6,1.4 L9.8,1.4 L9.8,0 L14,0 Z"
|
||||
id="OCR" fill-rule="nonzero"></path>
|
||||
<path d="M4.2,0 L0,0 L0,4.2 L4.2,4.2 L4.2,0 Z M2.8,1.4 L2.8,2.8 L1.4,2.8 L1.4,1.4 L2.8,1.4 Z" id="Path"
|
||||
fill-rule="nonzero"></path>
|
||||
<path d="M4.2,9.8 L0,9.8 L0,14 L4.2,14 L4.2,9.8 Z M2.8,11.2 L2.8,12.6 L1.4,12.6 L1.4,11.2 L2.8,11.2 Z" id="Path"
|
||||
fill-rule="nonzero"></path>
|
||||
<path d="M14,0 L9.8,0 L9.8,4.2 L14,4.2 L14,0 Z M12.6,1.4 L12.6,2.8 L11.2,2.8 L11.2,1.4 L12.6,1.4 Z" id="Path"
|
||||
fill-rule="nonzero"></path>
|
||||
<path d="M14,9.8 L9.8,9.8 L9.8,14 L14,14 L14,9.8 Z M12.6,11.2 L12.6,12.6 L11.2,12.6 L11.2,11.2 L12.6,11.2 Z" id="Path"
|
||||
fill-rule="nonzero"></path>
|
||||
<rect id="Rectangle" x="4.2" y="1.4" width="5.6" height="1.4"></rect>
|
||||
<rect id="Rectangle" x="4.2" y="11.2" width="5.6" height="1.4"></rect>
|
||||
<rect id="Rectangle" x="11.2" y="4.2" width="1.4" height="5.6"></rect>
|
||||
<rect id="Rectangle" x="1.4" y="4.2" width="1.4" height="5.6"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@ -1,5 +1,5 @@
|
||||
import { IListable } from '@iqser/common-ui';
|
||||
import { FileAttributeConfigType } from '@red/domain';
|
||||
import { FileAttributeConfigType } from './file-attribute-config';
|
||||
|
||||
export interface IField extends IListable {
|
||||
id: string;
|
||||
|
||||
@ -37,7 +37,7 @@ export class File implements IFile, IListable {
|
||||
readonly lastUpdated?: string;
|
||||
readonly lastUploaded?: string;
|
||||
readonly legalBasisVersion?: number;
|
||||
readonly numberOfAnalyses?: number;
|
||||
readonly numberOfAnalyses: number;
|
||||
readonly numberOfPages?: number;
|
||||
readonly rulesVersion?: number;
|
||||
readonly status: FileStatus;
|
||||
@ -110,7 +110,7 @@ export class File implements IFile, IListable {
|
||||
this.isUnderReview = this.status === FileStatuses.UNDER_REVIEW;
|
||||
this.isUnderApproval = this.status === FileStatuses.UNDER_APPROVAL;
|
||||
this.canBeApproved = !this.analysisRequired && !this.hasSuggestions;
|
||||
this.canBeOpened = !this.isError && !this.isPending;
|
||||
this.canBeOpened = !this.isError && !this.isPending && this.numberOfAnalyses > 0;
|
||||
this.isWorkable = !this.isProcessing && this.canBeOpened;
|
||||
this.canBeOCRed = !this.excluded && !this.lastOCRTime && ['UNASSIGNED', 'UNDER_REVIEW', 'UNDER_APPROVAL'].includes(this.status);
|
||||
|
||||
|
||||
@ -117,7 +117,7 @@ export interface IFile {
|
||||
/**
|
||||
* The number of times the file has been analyzed.
|
||||
*/
|
||||
readonly numberOfAnalyses?: number;
|
||||
readonly numberOfAnalyses: number;
|
||||
/**
|
||||
* The number of pages of the file.
|
||||
*/
|
||||
|
||||
@ -9,3 +9,4 @@ export * from './remove-redaction.request';
|
||||
export * from './manual-add.response';
|
||||
export * from './approve-request';
|
||||
export * from './image-recategorization.request';
|
||||
export * from './resize.request';
|
||||
|
||||
8
libs/red-domain/src/lib/redaction-log/resize.request.ts
Normal file
8
libs/red-domain/src/lib/redaction-log/resize.request.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { IRectangle } from '../geometry';
|
||||
|
||||
export interface IResizeRequest {
|
||||
annotationId: string;
|
||||
comment: string;
|
||||
positions: IRectangle[];
|
||||
value: string;
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "redaction",
|
||||
"version": "2.345.0",
|
||||
"version": "2.346.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user