From 126d47f9e45a5865ba8993a29e30ba43002fd4b4 Mon Sep 17 00:00:00 2001 From: amentis Date: Tue, 28 Nov 2023 19:10:04 +0200 Subject: [PATCH] add language file fallback method --- .../java/eu/eudat/data/LanguageEntity.java | 2 +- .../eudat/model/persist/LanguagePersist.java | 2 - .../service/language/LanguageService.java | 3 + .../service/language/LanguageServiceImpl.java | 24 ++- .../controllers/v2/LanguageV2Controller.java | 9 +- .../updates/00.01.024_addLanguage.sql | 2 +- .../services/language/language-v2.service.ts | 9 + .../editor/language-editor.component.html | 4 +- .../editor/language-editor.component.ts | 19 +- .../language/editor/language-editor.model.ts | 2 +- .../language-editor.component.html | 30 --- .../language-editor.component.scss | 51 ----- .../language-editor.component.ts | 176 ------------------ .../language-editor/language-editor.module.ts | 21 --- .../language-editor.routing.ts | 16 -- 15 files changed, 61 insertions(+), 309 deletions(-) delete mode 100644 dmp-frontend/src/app/ui/language-editor/language-editor.component.html delete mode 100644 dmp-frontend/src/app/ui/language-editor/language-editor.component.scss delete mode 100644 dmp-frontend/src/app/ui/language-editor/language-editor.component.ts delete mode 100644 dmp-frontend/src/app/ui/language-editor/language-editor.module.ts delete mode 100644 dmp-frontend/src/app/ui/language-editor/language-editor.routing.ts diff --git a/dmp-backend/core/src/main/java/eu/eudat/data/LanguageEntity.java b/dmp-backend/core/src/main/java/eu/eudat/data/LanguageEntity.java index d244f670f..525998fe1 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/data/LanguageEntity.java +++ b/dmp-backend/core/src/main/java/eu/eudat/data/LanguageEntity.java @@ -20,7 +20,7 @@ public class LanguageEntity { private String code; public static final String _code = "code"; - @Column(name = "payload", nullable = false) + @Column(name = "payload") private String payload; public static final String _payload = "payload"; diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/LanguagePersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/LanguagePersist.java index f328a44a1..c24e7ada1 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/persist/LanguagePersist.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/LanguagePersist.java @@ -16,8 +16,6 @@ public class LanguagePersist { @Size(max = 20, message = "{validation.largerthanmax}") private String code; - @NotNull(message = "{validation.empty}") - @NotEmpty(message = "{validation.empty}") private String payload; private String hash; diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/language/LanguageService.java b/dmp-backend/core/src/main/java/eu/eudat/service/language/LanguageService.java index f072b0b38..e6f059609 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/language/LanguageService.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/language/LanguageService.java @@ -9,11 +9,14 @@ import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.FieldSet; import javax.management.InvalidApplicationException; +import java.io.IOException; import java.util.UUID; public interface LanguageService { Language persist(LanguagePersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException; + String getPayload(String code) throws IOException; + void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/language/LanguageServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/language/LanguageServiceImpl.java index 0cac30f44..113e3f2eb 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/language/LanguageServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/language/LanguageServiceImpl.java @@ -26,9 +26,16 @@ import jakarta.persistence.EntityManager; import org.slf4j.LoggerFactory; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import javax.management.InvalidApplicationException; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.List; import java.util.UUID; @@ -44,11 +51,12 @@ public class LanguageServiceImpl implements LanguageService { private final ConventionService conventionService; private final MessageSource messageSource; private final ErrorThesaurusProperties errors; + private final Environment environment; public LanguageServiceImpl( EntityManager entityManager, AuthorizationService authorizationService, DeleterFactory deleterFactory, BuilderFactory builderFactory, - ConventionService conventionService, MessageSource messageSource, ErrorThesaurusProperties errors){ + ConventionService conventionService, MessageSource messageSource, ErrorThesaurusProperties errors, Environment environment){ this.entityManager = entityManager; this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; @@ -56,6 +64,7 @@ public class LanguageServiceImpl implements LanguageService { this.conventionService = conventionService; this.messageSource = messageSource; this.errors = errors; + this.environment = environment; } @@ -92,6 +101,19 @@ public class LanguageServiceImpl implements LanguageService { return this.builderFactory.builder(LanguageBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, Language._id), data); } + public String getPayload(String code) throws IOException { + this.authorizationService.authorizeForce(Permission.BrowseLanguage); + + String fileName = this.environment.getProperty("language.path") + code + ".json"; + InputStream is = new FileInputStream(fileName); + + byte[] content = new byte[is.available()]; + is.read(content); + is.close(); + + return new String(content, StandardCharsets.UTF_8); + } + public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { logger.debug("deleting : {}", id); diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/LanguageV2Controller.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/LanguageV2Controller.java index 9af208ed5..9e5581213 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/LanguageV2Controller.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/LanguageV2Controller.java @@ -3,6 +3,7 @@ package eu.eudat.controllers.v2; import com.fasterxml.jackson.core.JsonProcessingException; import eu.eudat.audit.AuditableAction; import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.authorization.Permission; import eu.eudat.data.LanguageEntity; import eu.eudat.model.Language; import eu.eudat.model.builder.LanguageBuilder; @@ -33,6 +34,7 @@ import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.web.bind.annotation.*; import javax.management.InvalidApplicationException; +import java.io.IOException; import java.util.AbstractMap; import java.util.List; import java.util.Map; @@ -50,7 +52,6 @@ public class LanguageV2Controller { private final MessageSource messageSource; private final AuthorizationService authorizationService; private final LanguageService languageService; - @Autowired public LanguageV2Controller( BuilderFactory builderFactory, @@ -104,16 +105,20 @@ public class LanguageV2Controller { } @GetMapping("code/{code}") - public Language get(@PathVariable("code") String code, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException { + public Language get(@PathVariable("code") String code, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, IOException { logger.debug(new MapLogEntry("retrieving" + Language.class.getSimpleName()).And("code", code).And("fields", fieldSet)); this.censorFactory.censor(LanguageCensor.class).censor(fieldSet, null); LanguageQuery query = this.queryFactory.query(LanguageQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).codes(code); Language model = this.builderFactory.builder(LanguageBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(fieldSet, query.firstAs(fieldSet)); + if (model == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{code, Language.class.getSimpleName()}, LocaleContextHolder.getLocale())); + if (model.getPayload() == null){ + model.setPayload(this.languageService.getPayload(code)); + } this.auditService.track(AuditableAction.Language_Lookup, Map.ofEntries( new AbstractMap.SimpleEntry("code", code), new AbstractMap.SimpleEntry("fields", fieldSet) diff --git a/dmp-db-scema/updates/00.01.024_addLanguage.sql b/dmp-db-scema/updates/00.01.024_addLanguage.sql index c94160411..2237e1f81 100644 --- a/dmp-db-scema/updates/00.01.024_addLanguage.sql +++ b/dmp-db-scema/updates/00.01.024_addLanguage.sql @@ -8,7 +8,7 @@ BEGIN ( id uuid NOT NULL, code character varying(20) NOT NULL, - payload text NOT NULL, + payload text, created_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, is_active smallint NOT NULL, diff --git a/dmp-frontend/src/app/core/services/language/language-v2.service.ts b/dmp-frontend/src/app/core/services/language/language-v2.service.ts index d8cc12630..1648b13e4 100644 --- a/dmp-frontend/src/app/core/services/language/language-v2.service.ts +++ b/dmp-frontend/src/app/core/services/language/language-v2.service.ts @@ -35,6 +35,15 @@ export class LanguageV2Service { catchError((error: any) => throwError(error))); } + getSingleWithCode(code: string, reqFields: string[] = []): Observable { + const url = `${this.apiBase}/code/${code}`; + const options = { params: { f: reqFields } }; + + return this.http + .get(url, options).pipe( + catchError((error: any) => throwError(error))); + } + persist(item: LanguagePersist): Observable { const url = `${this.apiBase}/persist`; diff --git a/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.html b/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.html index 47c09eb28..f4692f418 100644 --- a/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.html +++ b/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.html @@ -37,10 +37,10 @@ {{'GENERAL.VALIDATION.REQUIRED' | translate}} -
+
{{'LANGUAGE-EDITOR.FIELDS.PAYLOAD' | translate}} - + {{'GENERAL.VALIDATION.REQUIRED' | translate}} diff --git a/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.ts b/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.ts index 4b8554420..96c640b95 100644 --- a/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.ts @@ -43,8 +43,6 @@ export class LanguageEditorComponent extends BaseEditor { + data.payload = language.payload; + this.editorModel = data ? new LanguageEditorModel().fromModel(data) : new LanguageEditorModel(); + this.isDeleted = data ? data.isActive === IsActive.Inactive : false; + this.buildForm(); + }); + }else{ + this.editorModel = data ? new LanguageEditorModel().fromModel(data) : new LanguageEditorModel(); + this.isDeleted = data ? data.isActive === IsActive.Inactive : false; + this.buildForm(); + } } catch (error) { this.logger.error('Could not parse Language item: ' + data + error); this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error); diff --git a/dmp-frontend/src/app/ui/admin/language/editor/language-editor.model.ts b/dmp-frontend/src/app/ui/admin/language/editor/language-editor.model.ts index c536ff745..ff66e8802 100644 --- a/dmp-frontend/src/app/ui/admin/language/editor/language-editor.model.ts +++ b/dmp-frontend/src/app/ui/admin/language/editor/language-editor.model.ts @@ -41,7 +41,7 @@ export class LanguageEditorModel extends BaseEditorModel implements LanguagePers const baseValidationArray: Validation[] = new Array(); baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] }); baseValidationArray.push({ key: 'code', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'code')] }); - baseValidationArray.push({ key: 'payload', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'payload')] }); + baseValidationArray.push({ key: 'payload', validators: [BackendErrorValidator(this.validationErrorModel, 'payload')] }); baseValidationArray.push({ key: 'hash', validators: [] }); baseContext.validation = baseValidationArray; diff --git a/dmp-frontend/src/app/ui/language-editor/language-editor.component.html b/dmp-frontend/src/app/ui/language-editor/language-editor.component.html deleted file mode 100644 index 87d9f0168..000000000 --- a/dmp-frontend/src/app/ui/language-editor/language-editor.component.html +++ /dev/null @@ -1,30 +0,0 @@ -
-
-
- - -
- -
-
- - {{key}} : - - -
-
-
-
- -
- -
-
diff --git a/dmp-frontend/src/app/ui/language-editor/language-editor.component.scss b/dmp-frontend/src/app/ui/language-editor/language-editor.component.scss deleted file mode 100644 index c92bc2ed4..000000000 --- a/dmp-frontend/src/app/ui/language-editor/language-editor.component.scss +++ /dev/null @@ -1,51 +0,0 @@ -.language-editor { - padding-top: 1em; - padding-bottom: 2em; - - .language-area { - box-sizing: content-box; - } - - .save-btn { - padding-top: inherit !important; - top: auto !important; - width: 56px !important; - bottom: 10px; - position: fixed; - right: 24px; - } - - .sticky { - position: fixed; - left: 214px; - right: 214px; - width: 50%; - } - - .search-bar { - padding-top: inherit !important; - bottom: auto !important; - width: 258px !important; - top: 100px; - position: fixed; - right: 24px; - background-color: white; - border: 1px solid rgb(218, 218, 218); - border-radius: 6px; - padding-left: 10px; - } - - .search-text { - border: 0px solid rgb(218, 218, 218); - border-radius: 6px; - width: 180px; - } - .search-text::placeholder { - color: rgb(197, 194, 194); - line-height: 150%; - } - - .search-btn { - left: 4px; - } -} diff --git a/dmp-frontend/src/app/ui/language-editor/language-editor.component.ts b/dmp-frontend/src/app/ui/language-editor/language-editor.component.ts deleted file mode 100644 index d0e52615a..000000000 --- a/dmp-frontend/src/app/ui/language-editor/language-editor.component.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { Component, OnInit, OnDestroy, ViewChild, AfterViewInit, ChangeDetectorRef, AfterViewChecked, ElementRef, ViewChildren, QueryList } from '@angular/core'; -import { LanguageService } from '@app/core/services/language/language.service'; -import { BaseComponent } from '@common/base/base.component'; -import { takeUntil } from 'rxjs/operators'; -import { UntypedFormGroup, UntypedFormBuilder } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; -import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service'; -import { Router } from '@angular/router'; -import { CdkTextareaAutosize } from '@angular/cdk/text-field'; -import { MatomoService } from '@app/core/services/matomo/matomo-service'; -import { HttpClient } from '@angular/common/http'; - -@Component({ - selector: 'app-language-editor', - templateUrl: './language-editor.component.html', - styleUrls: ['./language-editor.component.scss'] -}) -export class LanguageEditorComponent extends BaseComponent implements OnInit, AfterViewInit, OnDestroy { - - @ViewChildren('autosize', {read: CdkTextareaAutosize}) elements: QueryList; - - readonly rowHeight = 100; - readonly maxElements = 10; - - allkeys = []; - keys = []; - visibleKeys = []; - startIndex = 0; - endIndex: number; - parseFinished = false; - currentLang: string; - formGroup: UntypedFormGroup; - formBuilder: UntypedFormBuilder; - - constructor( - private language: LanguageService, - private uiNotificationService: UiNotificationService, - private translate: TranslateService, - private router: Router, - private httpClient: HttpClient, - private matomoService: MatomoService - ) { super(); } - - - ngAfterViewInit(): void { - this.elements.changes.pipe(takeUntil(this._destroyed)).subscribe(items => { - if (this.elements.length > 0) { - this.elements.toArray().forEach(autosize => { - if (autosize instanceof CdkTextareaAutosize) { - this.setupAutosize(autosize); - } else { - console.log(autosize); - } - }) - } - }); - } - - ngOnInit() { - this.matomoService.trackPageView('Admin: Language Editor'); - this.formBuilder = new UntypedFormBuilder(); - this.formGroup = this.formBuilder.group({}); - this.endIndex = this.maxElements; - window.addEventListener('scroll', this.refreshFn, true); - this.language.getCurrentLanguageJSON() - .pipe(takeUntil(this._destroyed)) - .subscribe(response => { - const blob = new Blob([response.body], { type: 'application/json' }); - this.convertBlobToJSON(blob); - - }); - } - - ngOnDestroy() { - window.removeEventListener('scroll', this.refreshFn, true); - } - - convertBlobToJSON(blob: Blob) { - const fr = new FileReader(); - fr.onload = ev => { - const langObj = JSON.parse(fr.result as string); - this.convertObjectToForm(langObj, '', this.formGroup); - this.currentLang = this.language.getCurrentLanguageName(); - this.keys.length = 0; - for (const key of this.allkeys) { - this.keys.push(key); - } - this.visibleKeys = this.keys.slice(this.startIndex, this.endIndex); - this.parseFinished = true; - - }; - fr.readAsText(blob); - } - - convertObjectToForm(obj: any, parentKey: string, form: UntypedFormGroup) { - for (let prop in obj) { - const key = parentKey !== '' ? `${parentKey}.${prop}` : prop; - if (typeof obj[prop] === 'object') { - form.addControl(prop, this.formBuilder.group({})); - this.convertObjectToForm(obj[prop], key, form.get(prop) as UntypedFormGroup); - continue; - } else { - form.addControl(prop, this.formBuilder.control(obj[prop])); - this.allkeys.push(key); - } - } - return; - } - - updateLang() { - const json = JSON.stringify(this.formGroup.value, null, " "); - this.language.updateLanguage(json).pipe(takeUntil(this._destroyed)) - .subscribe( - complete => { - this.onCallbackSuccess(complete); - }, - error => { - this.onCallbackError(error); - } - ); - - } - - onCallbackSuccess(id?: String): void { - this.uiNotificationService.snackBarNotification(this.translate.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); - this.router.navigate(['/reload']).then(() => this.router.navigate(['/language-editor'])); - } - - onCallbackError(error: any) { - this.uiNotificationService.snackBarNotification(error, SnackBarNotificationLevel.Error); - //this.validateAllFormFields(this.formGroup); - } - - refreshFn = (ev: Event) => { - const evDoc = (ev.target); - let mainPage; - evDoc.childNodes.forEach(child => { - if (( child).id === 'main-page') { - mainPage = child; - } - }); - if (document.scrollingElement !== undefined && mainPage !== undefined) { - this.startIndex = Math.floor(evDoc.scrollTop / this.rowHeight); - this.endIndex = this.startIndex + this.maxElements; - const tempKeys = this.keys.slice(this.startIndex, this.endIndex); - this.visibleKeys.length = 0; - for (const key of tempKeys) { - this.visibleKeys.push(key); - } - } - } - - findKeys(ev: any) { - let tempKeys = []; - if (ev.value === "") { - tempKeys = this.allkeys; - } else { - tempKeys = this.allkeys.filter((key) => (this.formGroup.get(key).value as string).toLowerCase().includes(ev.value.toLowerCase())); - window.scrollTo({ top: 0 }); - } - this.keys.length = 0; - for (const key of tempKeys) { - this.keys.push(key); - } - this.visibleKeys = this.keys.slice(this.startIndex, this.endIndex); - } - - private setupAutosize(autosize: CdkTextareaAutosize) { - if (autosize !== undefined && autosize !== null) { - autosize.minRows = 1; - autosize.maxRows = 5; - autosize.enabled = true; - } - } - -} diff --git a/dmp-frontend/src/app/ui/language-editor/language-editor.module.ts b/dmp-frontend/src/app/ui/language-editor/language-editor.module.ts deleted file mode 100644 index 9d8b40f16..000000000 --- a/dmp-frontend/src/app/ui/language-editor/language-editor.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NgModule } from '@angular/core'; -import { LanguageEditorComponent } from './language-editor.component'; -import { LanguageEditorRoutingModule } from './language-editor.routing'; -import { CommonUiModule } from '@common/ui/common-ui.module'; -import { CommonFormsModule } from '@common/forms/common-forms.module'; -import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module'; -import {TextFieldModule} from '@angular/cdk/text-field'; - - - -@NgModule({ - declarations: [LanguageEditorComponent], - imports: [ - CommonUiModule, - CommonFormsModule, - ConfirmationDialogModule, - LanguageEditorRoutingModule, - TextFieldModule - ] -}) -export class LanguageEditorModule { } diff --git a/dmp-frontend/src/app/ui/language-editor/language-editor.routing.ts b/dmp-frontend/src/app/ui/language-editor/language-editor.routing.ts deleted file mode 100644 index fde8ad57b..000000000 --- a/dmp-frontend/src/app/ui/language-editor/language-editor.routing.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { LanguageEditorComponent } from './language-editor.component'; -import { AuthGuard } from '@app/core/auth-guard.service'; -import { AdminAuthGuard } from '@app/core/admin-auth-guard.service'; - -const routes: Routes = [ - { path: '', component: LanguageEditorComponent, canActivate: [AdminAuthGuard] }, - // { path: ':id', component: UserProfileComponent } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class LanguageEditorRoutingModule { }