From 2637f399e2d1f337d63b2d9aa2559d73aca63bfd Mon Sep 17 00:00:00 2001 From: RIHTARSIC Joze <joze.rihtarsic@ext.ec.europa.eu> Date: Tue, 3 Sep 2024 14:23:40 +0200 Subject: [PATCH] [EDELIVERY-12747] add configuration options for update/create user alerts and fix language loading from filesystem --- smp-angular/src/assets/i18n/ro.json | 852 ------------------ .../smp/config/enums/SMPPropertyEnum.java | 25 + .../init/SMPLocaleFileSystemInitializer.java | 8 +- .../ec/edelivery/smp/i18n/SMPLocale.java | 47 - .../smp/services/ConfigurationService.java | 30 + .../smp/services/CredentialsAlertService.java | 18 +- .../services/SMPLanguageResourceService.java | 286 ++++++ .../smp/services/SMPLocaleService.java | 71 -- .../services/mail/MailTemplateService.java | 43 +- .../src/main/resources/mail-messages/en.json | 22 + .../mail-messages/mail-messages_en.properties | 99 -- .../SMPLanguageResourceServiceTest.java | 137 +++ .../smp/services/mail/MailTemplateTest.java | 31 +- .../META-INF/resources/ui/assets/i18n/en.json | 6 + .../smp/ui/external/LocaleController.java | 34 +- .../smp/ui/internal/UserAdminController.java | 1 + 16 files changed, 573 insertions(+), 1137 deletions(-) delete mode 100644 smp-angular/src/assets/i18n/ro.json delete mode 100644 smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/i18n/SMPLocale.java create mode 100644 smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/SMPLanguageResourceService.java delete mode 100644 smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/SMPLocaleService.java create mode 100644 smp-server-library/src/main/resources/mail-messages/en.json delete mode 100644 smp-server-library/src/main/resources/mail-messages/mail-messages_en.properties create mode 100644 smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/SMPLanguageResourceServiceTest.java create mode 100644 smp-server-library/src/test/resources/META-INF/resources/ui/assets/i18n/en.json diff --git a/smp-angular/src/assets/i18n/ro.json b/smp-angular/src/assets/i18n/ro.json deleted file mode 100644 index c9d1c25a4..000000000 --- a/smp-angular/src/assets/i18n/ro.json +++ /dev/null @@ -1,852 +0,0 @@ -{ - - "column.selection.link.all": "Tot", - "column.selection.link.none": "Nimic", - "column.selection.link.show": "Toate coloanele", - "column.selection.link.hide": "Ascunde coloanele", - - "cancel.dialog.text": "Doriti sa anulati toate modificarile care nu au fost inca salvate?", - "cancel.dialog.title": "Modificari in Curs", - - "certificate.dialog.button.close": "Inchide", - "certificate.dialog.title": "Detalii Certificat", - - "confirmation.dialog.button.no": "Nu", - "confirmation.dialog.button.yes": "Da", - - "credentials.dialog.button.close": "Inchide", - "credentials.dialog.button.copy.access.token": "Copiaza valoarea Tokenului de access", - "credentials.dialog.button.import": "Importa", - "credentials.dialog.button.generate.access.token": "Genereaza un Token de access", - "credentials.dialog.button.save.certificate": "Salveaza Certificatul", - "credentials.dialog.error.read.certificate": "O eroare a fost detectata la citirea Certificatului. Verificati daca fisierul incarcat este un Certificat valid", - "credentials.dialog.error.upload.certificate": "O eroare a fost detectata la incarcarea Certificatului [{{fileName}}]. {{errorDescription}}", - "credentials.dialog.success.generate.token": "Tokenul cu identificatorul: \"{{responseIdentifier}}\" si valoarea: \"{{responseValue}}\" a fost generat!<br /><br />Copiati valoarea Tokenului de access si salvati-o intr-un loc sigur.<br /><br />Nu veti mai putea sa obtineti valoarea Tokenului de access o data ce apasati pe <b>Inchide</b>.", - "credentials.dialog.label.active": "Activ", - "credentials.dialog.label.certificate.validity.period": "Perioada de valabilitate a Certificatului", - "credentials.dialog.label.description": "Descriere", - "credentials.dialog.label.subject.name": "Subiect", - "credentials.dialog.label.smp.certificate.id": " Identificator certificat SMP", - "credentials.dialog.label.invalid.from": "Data \"Activ de la\" invalida", - "credentials.dialog.label.invalid.expire.on": "Data \"Expira la\" invalida", - "credentials.dialog.label.issuer.name": "Nume emitent", - "credentials.dialog.label.serial.number": "Numar de serie", - "credentials.dialog.label.validity": "Periada de valabilitate", - "credentials.dialog.placeholder.end.date": "Data finala", - "credentials.dialog.placeholder.expire.on": "Expira la", - "credentials.dialog.placeholder.start.date": "Date initiala", - "credentials.dialog.placeholder.valid.from": "Valabil de la", - - "dialog.button.close": "Inchide", - "dialog.button.no": "Nu", - "dialog.button.ok": "OK", - "dialog.button.yes": "Da", - - "document.property.dialog.button.ok": "OK", - "document.property.dialog.button.cancel": "Anuleaza", - "document.property.dialog.error.property.already.exists": "Proprietatea exista deja!", - "document.property.dialog.label.close": "Inchide", - "document.property.dialog.label.property.description": "Descrierea proprietatii:", - "document.property.dialog.label.property.name": "Numele proprietatii:", - "document.property.dialog.label.property.type": "Tipul proprietatii:", - "document.property.dialog.label.property.value": "Valoarea proprietatii:", - "document.property.dialog.title.new.mode": "Creeaza Propietatea Document", - "document.property.dialog.title.edit.mode": "Schimbati Proprietatea Document", - - "expired.password.dialog.text": "Parola dumneavoastra este mai veche de trei luni. Va rugam sa o schimbati cat mai repede posibil!", - "expired.password.dialog.title": "Parola pe cale sa expire", - - "manage.members.dialog.button.close": "Inchide", - "manage.members.dialog.title.invite.mode": "Invita Membru {{membershipType}}", - "manage.members.dialog.title.edit.mode": "Modifica Membru {{membershipType}}", - - "member.dialog.button.close": "Inchide", - "member.dialog.button.save": "Salveaza", - "member.dialog.hint.choose.role": "Alege rolul membrului", - "member.dialog.hint.type.username": "Introduceti numele utilizatorului sau numele complet necesar localizarii utilizatorului si alegeti utilizatorul dorit din lista de rezultate", - "member.dialog.label.choose.user": "Alegeti Utilizatorul de invitat", - "member.dialog.label.invite.members": "Sunteti pe cale sa invitati membrii la {{target}}.", - "member.dialog.placeholder.role.type": "Rolul membrului", - "manage.dialog.title.invite.mode": "Invita membrul {{membershipType}}", - "manage.dialog.title.edit.mode": "Schimbati membrul {{membershipType}}", - "member.dialog.tooltip.role.type": "Tipul rolului detinut de membru.", - - "object.properties.dialog.button.close": "Inchide", - "object.properties.dialog.header.key": "Cheie", - "object.properties.dialog.header.value": "Valoare", - - "password.change.dialog.button.confirm": "Seteaza/modificati parola", - "password.change.dialog.button.close": "Inchide", - "password.change.dialog.error.old.password.reused": "Noua parola nu trebuie sa fie egala cu vechea parola actuala!", - "password.change.dialog.error.passwords.mismatch": "Confirmare parolei noi nu se potriveste cu noua parola!", - "password.change.dialog.label.confirm.new.password": "Confirmarea parolei noi", - "password.change.dialog.label.username": "Schimbati parola pentru Utilizator", - "password.change.dialog.label.new.password": "Parola noua", - "password.change.dialog.label.password.admin": "Parola de administrator pentru [{{username}}]", - "password.change.dialog.label.password.user": "Parola actuala", - "password.change.dialog.legend.required.fields": "* campuri obligatorii", - "password.change.dialog.title": "Seteaza/Schimba Parola", - "password.change.dialog.success.password.change.admin": "Parola a fost setata/schimbata cu success.", - "password.change.dialog.success.password.change.user": "Parola a fost setata/schimbata cu success. Conectati-va din nou la aplicatie folosind noua parola!", - - "property.details.dialog.button.ok": "OK", - "property.details.dialog.button.cancel": "Anuleaza", - "property.details.dialog.error.invalid.property": "Proprietate invalida", - "property.details.dialog.error.validation": "A aparut o eroare la Validarea proprietatii", - "property.details.dialog.label.use.system.default.value": "Utilizati valoarea implicata a Sistemului", - "property.details.dialog.label.property.value": "Valoarea Proprietatii:", - "property.details.dialog.legend.description": "Descriere:", - "property.details.dialog.title.edit.mode": "Modificaarea Proprietatii {{type}}", - "property.details.dialog.title.new.mode": "Proprietate Noua {{type}}", - - "session.expiration.dialog.button.expire": "Extinde", - "session.expiration.dialog.button.logout": "Deconectare", - "session.expiration.dialog.title": "Extindeti sesiunea", - "session.expiration.dialog.label.session.about.to.expire": "Sesiunea dvs. este pe cale sa expire in <b>{{timeLeft}}</b> secunde!<br />Doriti sa va deconectati acum sau sa o prelungiti pentru inca <b>{{timeout}}</b> secunde?", - - "alert.panel.title": "Alerte", - "alert.panel.user.title": "{{value}} (E-mail: '{{mailTo}}')", - - "certificate.panel.title": "Datele certificatului selectat", - "certificate.panel.label.alias": "Alias", - "certificate.panel.label.crl": "Adresa URL a listei de revocare a certificatelor", - "certificate.panel.label.issuer": "Emitent", - "certificate.panel.label.public.key.type": "Tipul cheii publice", - "certificate.panel.label.serial.number": "Numar de serie", - "certificate.panel.label.smp.certificate.id": "Identificator certificat SMP", - "certificate.panel.label.subject.name": "Numele subiectului", - "certificate.panel.label.valid.from": "Valabil de la", - "certificate.panel.label.valid.to": "Valabil pana la", - "certificate.panel.placeholder.alias": "Alias", - "certificate.panel.placeholder.crl": "Certificate revocation list URL", - "certificate.panel.placeholder.issuer": "Emitent", - "certificate.panel.placeholder.public.key.type": "Tipul cheii publice", - "certificate.panel.placeholder.smp.certificate.id": "Identificator certificat SMP", - "certificate.panel.placeholder.serial.number": "Numar de serie", - "certificate.panel.placeholder.subject.name": "Numele subiectului", - "certificate.panel.placeholder.valid.from": "Valabil pana la data", - "certificate.panel.placeholder.valid.to": "Valabil pana la data", - - "document.properties.panel.label.filter": "Filtru", - "document.properties.panel.label.property": "Proprietate", - "document.properties.panel.label.value": "Valoare", - "document.properties.panel.label.no.properties.found": "Nu s-au gasit proprietati ale Documentului", - "document.properties.panel.label.select.page": "Selectati pagina cu proprietati", - "document.properties.panel.label.expand.collapse": "Extinde/Restrange", - "document.properties.panel.tooltip.expand.collapse": "Extinde/Restrange panoul cu proprietati", - "document.properties.panel.label.create": "Creeaza", - "document.properties.panel.label.delete.remove": "Sterge", - "document.properties.panel.label.edit": "Schimba", - "document.properties.panel.label.reset": "Reseteaza modificarile", - "document.properties.panel.tooltip.create": "Creeaza proprietate noua", - "document.properties.panel.tooltip.edit": "Schimba proprietate", - "document.properties.panel.tooltip.delete.remove": "Sterge proprietatea selectata", - "document.properties.panel.tooltip.reset": "Reseteaza modificarile", - - "membership.panel.button.invite.member": "Invita membru", - "membership.panel.button.edit.membership": "Schimba", - "membership.panel.button.delete.membership": "Sterge", - "membership.panel.delete.confirmation.dialog.title": "Sterge membru", - "membership.panel.delete.confirmation.dialog.description": "Actiunea va sterge membrul [\"{{username}}\"]!<br/><br/>Doriti sa continuati?", - "membership.panel.label.filter": "Filtru membri", - "membership.panel.label.full.name": "Numele complet", - "membership.panel.label.member.of": "Membru al", - "membership.panel.label.no.filter.results": "Niciun membru direct care sa se potriveasca cu filtrul \"{{filterValue}}\"", - "membership.panel.label.no.data.found": "Fara membri directi pentru domeniu", - "membership.panel.label.role.type": "Tipul rolului", - "membership.panel.label.username": "Nume de utilizator", - "membership.panel.placeholder.filter": "Filtru membri", - "membership.panel.title.domain": "Membrii directi de Domeniu {{value}}", - "membership.panel.title.group": "Membrii directi de Grup {{value}}", - "membership.panel.title.resource": "Membrii directi de Resursa", - "membership.panel.tooltip.invite.member": "Invita membru nou", - "membership.panel.tooltip.edit.membership": "Schimba calitatea de membru a utilizatorului selectat", - "membership.panel.tooltip.delete.membership": "Sterge calitatea de membru selectata", - - "user.profile.panel.button.reset": "Reseteaza", - "user.profile.panel.button.save": "Salveaza", - "user.profile.panel.button.update.password": "Setati/modificati parola", - "user.profile.panel.hint.choose.member.role": "Alegeti rolul membrului", - "user.profile.panel.error.email": "Adresa de e-mail este invalida!", - "user.profile.panel.label.active": "Activ", - "user.profile.panel.label.application.role": "Rolul in aplicatie", - "user.profile.panel.label.email": "Adresa de e-mail", - "user.profile.panel.label.example": "Exemplu de data/ora", - "user.profile.panel.label.full.name": "Nume complet (nume si prenume)", - "user.profile.panel.label.language.bg": "Bulgara", - "user.profile.panel.label.language.cs": "Ceha", - "user.profile.panel.label.language.da": "Daneza", - "user.profile.panel.label.language.de": "Germana", - "user.profile.panel.label.language.el": "Greaca", - "user.profile.panel.label.language.en": "Engleza", - "user.profile.panel.label.language.es": "Spaniola", - "user.profile.panel.label.language.et": "Estoniana", - "user.profile.panel.label.language.fi": "Finlandeza", - "user.profile.panel.label.language.fr": "Franceza", - "user.profile.panel.label.language.hr": "Croata", - "user.profile.panel.label.language.hu": "Maghiara", - "user.profile.panel.label.language.it": "Italiana", - "user.profile.panel.label.language.lt": "Lituaniana", - "user.profile.panel.label.language.lv": "Letona", - "user.profile.panel.label.language.mt": "Malteza", - "user.profile.panel.label.language.nl": "Olandeza", - "user.profile.panel.label.language.pl": "Poloneza", - "user.profile.panel.label.language.pt": "Portugheza", - "user.profile.panel.label.language.ro": "Romana", - "user.profile.panel.label.language.sk": "Slovaca", - "user.profile.panel.label.language.sl": "Slovena", - "user.profile.panel.label.language.sv": "Suedeza", - "user.profile.panel.label.last.set": "Modificat ultima data la", - "user.profile.panel.label.locale": "Local (fomatare data/ora)", - "user.profile.panel.label.login.failed.attempts": "Incercari esuate", - "user.profile.panel.label.login.last.failed.attempt": "Ultima incercare esuata", - "user.profile.panel.label.new.user": "Va rugam sa completati detaliile utilizatorului si sa dati click pe \"Salveaza\" pentru a crea un nou utilizator", - "user.profile.panel.label.username": "Nume utilizator", - "user.profile.panel.label.password.expiration.date": "Parola expira la", - "user.profile.panel.label.suspended.until.date": "Dezactivat pana la", - "user.profile.panel.label.theme": "Tema", - "user.profile.panel.placeholder.choose.date": "Alegeti o data", - "user.profile.panel.placeholder.password.expiration.date": "Valid pana la", - "user.profile.panel.placeholder.login.failed.attempts": "Incercari esuate", - "user.profile.panel.text.account": "Date cont", - "user.profile.panel.text.credentials": "Resetati numele de utilizator si parola acestuia pentru autentificarea in aplicatie", - "user.profile.panel.text.user.profile": "Datele si setarile profilului utilizatorului", - "user.profile.panel.title.account": "Cont", - "user.profile.panel.title.credentials": "Nume de utilizator/parola", - "user.profile.panel.title.user.profile": "Profil utilizator", - "user.profile.panel.tooltip.application.role": "Rolul in aplicatie al utilizatorului", - "user.profile.panel.tooltip.password.expiration.date": "Parola implicita setata de Administratorul Sistemului! Utilizatorul trebuie sa isi schimbe parola imediat!", - "user.profile.panel.value.password.expiration.date": "Parola implicita sau nula", - - "row.limiter.placeholder": "Randuri", - - "search.table.button.cancel": "Anuleaza", - "search.table.button.delete": "Sterge", - "search.table.button.edit": "Modifica", - "search.table.button.new": "Nou rand", - "search.table.button.save": "Salveaza", - "search.table.button.search": "Cauta", - "search.table.dirty.confirmation.dialog.title": "Datele nu sunt inca salvate", - "search.table.dirty.confirmation.dialog.description": "Actiunea va reimprospata toate datele, iar datele care nu sunt salvate se vor pierde.<br/><br/>Doriti să continuati?", - "search.table.error.delete": "Eroare la stergere", - "search.table.error.update": "Operatia 'actualizare' nu a fost finalizata cu succes.", - "search.table.tooltip.edit": "Modifica", - "search.table.tooltip.delete": "Sterge", - "search.table.success.update": "Operatia 'actualizare' a fost finalizata cu succes", - - "group.dialog.button.close": "Inchide", - "group.dialog.button.save": "Salveaza", - "group.dialog.description": "Pentru a crea un grup nou, introduceti un nume unic de grup corespunzator domeniului si dati clic pe Salvare.", - "group.dialog.hint.group.name": "Nume unic de grup corespunzator domeniului", - "group.dialog.hint.group.visibility": "Alegeti vizibilitatea grupului", - "group.dialog.label.group.name": "Numele grupului", - "group.dialog.label.group.description": "Descriere grupului", - "group.dialog.label.group.visibility": "Vizibilitatea grupului", - "group.dialog.placeholder.group.visibility": "Vizibilitatea grupului", - "group.dialog.tooltip.group.visibility": "Vizibilitatea grupului.", - - "domain.group.button.create": "Creeaza", - "domain.group.button.delete": "Sterge", - "domain.group.button.edit": "Modifica datele", - "domain.group.button.group.members": "Membrii grupului", - "domain.group.delete.confirmation.dialog.title": "Sterge Grupul {{groupName}} din DomiSMP", - "domain.group.delete.confirmation.dialog.description": "Actiunea va sterge definitiv grupul!<br/><br/>Doriti sa continuati?", - "domain.group.error.delete": "Nu se poate sterge grupul din cauza datelor de domeniu nevalide. Este selectat grupul?", - "domain.group.group.details.dialog.title": "Detaliile Grupului", - "domain.group.label.filter": "Filtru membri", - "domain.group.label.group.name": "Numele grupului", - "domain.group.label.group.description": "Descriere", - "domain.group.label.group.visibility": "Vizibilitate", - "domain.group.label.no.filter.results": "Niciun grup care sa se potriveasca cu filtrul \"{{filterValue}}\"", - "domain.group.label.no.data.found": "Domeniul nu are grupuri", - "domain.group.label.select.pages": "Selectati paginile", - "domain.group.manage.members.dialog.title": "Gestionarea Membrilor Resursei", - "domain.group.success.delete": "Grupul de domeniu [{{groupName}}] sters", - "domain.group.title.domains.groups": "Grupuri de domeniu", - "domain.group.title.domains.groups.for.domain.code": "Grupurile de domeniu pentru [{{domainCode}}]", - "domain.group.tooltip.create": "Creeaza un grup nou", - "domain.group.tooltip.delete": "Stergeti grupul selectat", - "domain.group.tooltip.edit": "Modificati grupul selectat", - "domain.group.tooltip.filter": "Filtru membri", - "domain.group.tooltip.group.members": "Membrii grup", - - "edit.domain.label.configuration": "Configurare", - "edit.domain.label.domain.code": "Cod de domeniu", - "edit.domain.label.domain.members": "Membrii domeniu", - "edit.domain.label.filter": "Filtru coduri de domeniu", - "edit.domain.label.group": "Grup", - "edit.domain.label.no.filter.results": "Nu exista date care sa se potriveasca cu filtrul \"{{filterValue}}\"", - "edit.domain.label.user.not.administrator": "Utilizatorul nu este Administrator al niciunui domeniu", - "edit.domain.placeholder.domain.code": "Cod de domeniu", - "edit.domain.text": "Panoul de administrare a domeniilor este un instrument pentru Administratorii de domenii cu care acestia pot gestiona membrii si grupurile acestor domenii", - "edit.domain.title": "Modifica Domenii", - - "resource.dialog.button.close": "Inchide", - "resource.dialog.button.save": "Salveaza", - "resource.dialog.description": "Pentru a crea a noua resursa introduceti identificatorul unic si schema dupa care dati click pe Salveaza.", - "resource.dialog.label.resource.type": "Tipul de resursa selectat", - "resource.dialog.label.resource.id": "Identificatorul resursei", - "resource.dialog.label.resource.id.mandatory": "Identificator participantului nu trebuie sa fie gol si trebuie sa contina pana la 255 de caractere.", - "resource.dialog.label.resource.id.already.exists": "Identificator participantului pentru schema data este deja definit in baza de date!", - "resource.dialog.label.resource.scheme": "Schema resursei", - "resource.dialog.label.resource.scheme.mandatory": "Schema participantului nu poate fi goala.", - "resource.dialog.label.resource.visibility": "Vizibilitatea resursei", - "resource.dialog.placeholder.resource.type": "Selectati tipul resursei", - "resource.dialog.placeholder.resource.visibility": "Vizibilitatea resursei", - "resource.dialog.tooltip.resource.type": "Selectati tipul resursei.", - "resource.dialog.tooltip.resource.visibility": "Vizibilitatea resursei.", - - "group.resource.panel.button.create": "Creeaza", - "group.resource.panel.button.delete": "Sterge", - "group.resource.panel.button.edit": "Modifica", - "group.resource.panel.button.members": "Membrii resursei", - "group.resource.panel.delete.confirmation.dialog.title": "Stergeti resursa cu schema din DomiSMP", - "group.resource.panel.delete.confirmation.dialog.description": "Actiunea va sterge definitiv resursa [{{identifierScheme}}] cu identificatorul: [{{identifierValue}}]!<br/><br/>Doriti sa continuati?", - "group.resource.panel.label.filter": "Filtru resurse", - "group.resource.panel.label.identifier.scheme": "Schema", - "group.resource.panel.label.identifier.value": "Identificator", - "group.resource.panel.label.no.filter.results": "Nicio resursa care sa se potriveasca cu filtrul \"{{filterValue}}\"", - "group.resource.panel.label.no.data.found": "Grupul nu are resurse", - "group.resource.panel.label.select.pages": "Selectati pagina", - "group.resource.panel.manage.members.dialog.title": "Gestionarea Membrilor Resursei", - "group.resource.panel.placeholder.filter": "Filtru resurse", - "group.resource.panel.resource.details.dialog.title": "Detaliile Resursei", - "group.resource.panel.error.delete.group": "Nu se poate sterge grupul din cauza datelor de domeniu nevalide. Este selectat grupul?", - "group.resource.panel.error.delete.resource": "Nu se poate sterge resursa din cauza datelor nevalide despre resursa. Este resursa selectata?", - "group.resource.panel.success.delete": "Resursa [{{identifierScheme}}] cu identificatorul [{{identifierValue}}] a fost stearsa.", - "group.resource.panel.title": "Resursele grupului {{groupName}}", - "group.resource.panel.tooltip.create": "Creeaza", - "group.resource.panel.tooltip.delete": "Sterge resursa selectata", - "group.resource.panel.tooltip.edit": "Modifica datele", - "group.resource.panel.tooltip.members": "Membrii grupului", - - "edit.group.label.resources": "Resurse", - "edit.group.label.members": "Membrii", - "edit.group.label.user.not.administrator": "Utilizatorul nu este Administrator al niciunui grup", - "edit.group.label.select.domain": "Grup selectat", - "edit.group.label.select.group": "Grupul selectat", - "edit.group.placeholder.select.domain": "Selecteaza domeniu", - "edit.group.placeholder.select.group": "Selecteaza grup", - "edit.group.text": "Panoul de administrare a grupurilor este un instrument pentru Administratorilor de grupuri cu care acestia pot gestiona membrii grupurilor si resursele", - "edit.group.title": "Modifica Grup", - "edit.group.tooltip.select.domain": "Domeniu selectat.", - "edit.group.tooltip.select.group": "Grup selectat.", - - "document.wizard.dialog.button.cancel": "Anuleaza", - "document.wizard.dialog.button.ok": "OK", - "document.wizard.dialog.title": "Expert Extensie Resursa", - - "resource.details.panel.label.resource.id": "Identificator resursa", - "resource.details.panel.label.resource.name": "Modifica documentul resursei", - "resource.details.panel.label.resource.scheme": "Schema resursa", - "resource.details.panel.label.resource.type": "Tip resursa", - "resource.details.panel.label.resource.visibility": "Vizibilitate resursa", - "resource.details.panel.label.resource.visibility.private": "Resursa privata este accesibila doar membrilor resursei!", - "resource.details.panel.label.resource.visibility.private.group": "Resursa apartine grupului privat. Doar membrii grupului si membrii grupului resursei o pot accesa!", - "resource.details.panel.label.resource.visibility.private.domain": "Resursa apartine domeniului privat. Doar membrii domeniului, membrii grupului domeniului si membrii grupului resursei o pot accesa!", - "resource.details.panel.label.resource.visibility.public": "Resursa este publica in grupul public si in domeniul public. Datele resursei sunt accesibile tuturor utilizatorilor.", - "resource.details.panel.button.edit": "Modifica documentul", - "resource.details.panel.placeholder.resource.type": "Tipul resursei pentru resursa", - "resource.details.panel.tooltip.resource.type": "Selectati tipul pentru resursa.", - "resource.details.panel.tooltip.resource.visibility": "Vizibilitatea resursei.", - "resource.details.panel.tooltip.show.resource": "Afisati resursa", - "resource.details.panel.title": "Resurse", - - "resource.document.panel.button.back": "Inapoi", - "resource.document.panel.button.cancel": "Anuleaza", - "resource.document.panel.button.document.wizard": "Expert document", - "resource.document.panel.button.generate": "Genereaza", - "resource.document.panel.button.validate": "Valideaza", - "resource.document.panel.button.save": "Salveaza", - "resource.document.panel.cancel.confirmation.dialog.description": "Doriti sa anulati toate modificarile din document?", - "resource.document.panel.cancel.confirmation.dialog.title": "Anulati modificarile", - "resource.document.panel.document.wizard.dialog.title": "Expert resursa", - "resource.document.panel.label.created.on": "Creat la:", - "resource.document.panel.label.show.version": "Afisati versiunea:", - "resource.document.panel.placeholder.version": "Toate versiunile documentului", - "resource.document.panel.success.save": "Documentul este salvat cu versiunea curenta [{{currentResourceVersion}}].", - "resource.document.panel.success.generate": "Documentul este generat.", - "resource.document.panel.success.valid": "Documentul este valid.", - "resource.document.panel.title": "Resurse", - "resource.document.panel.tooltip.document.wizard": "Afiseaza dialogul expert document", - "resource.document.panel.tooltip.generate": "Genereaza resursa", - "resource.document.panel.tooltip.save": "Salveaza resursa", - "resource.document.panel.tooltip.validate": "Valideaza resursa", - "resource.document.panel.tooltip.version": "Selecteaza versiunea de afisat.", - - "subresource.document.panel.button.back": "Inapoi", - "subresource.document.panel.button.cancel": "Anuleaza", - "subresource.document.panel.button.document.wizard": "Expert document", - "subresource.document.panel.button.generate": "Genereaza", - "subresource.document.panel.button.validate": "Valideaza", - "subresource.document.panel.button.save": "Salveaza", - "subresource.document.panel.cancel.confirmation.dialog.description": "Doriti sa anulati toate modificarile din document?", - "subresource.document.panel.cancel.confirmation.dialog.title": "Anulati modificarile", - "subresource.document.panel.label.created.on": "Creat la:", - "subresource.document.panel.label.show.version": "Afisati versiunea:", - "subresource.document.panel.placeholder.show.version": "Toate versiunile documentului", - "subresource.document.panel.success.generate": "Documentul este generat.", - "subresource.document.panel.success.save": "Documentul este salvat cu versiunea curenta [{{currentResourceVersion}}].", - "subresource.document.panel.success.valid": "Documentul este valid.", - "subresource.document.panel.tooltip.document.wizard": "Afiseaza dialogul expert document", - "subresource.document.panel.tooltip.generate": "Genereaza resursa", - "subresource.document.panel.tooltip.save": "Salveaza resursa", - "subresource.document.panel.tooltip.show.version": "Selecteaza versiunea de afisat.", - "subresource.document.panel.tooltip.validate": "Valideaza resursa", - - "subresource.document.wizard.button.cancel": "Anuleaza", - "subresource.document.wizard.button.ok": "OK", - "subresource.document.wizard.error.read": "A aparut o eroare la citirea certificatului. Verificati daca fisierul incarcat are un tip de certificat valid", - "subresource.document.wizard.error.upload": "A aparut o eroare la incarcarea fisierului certificat [{{fileName}}] {{errorDescription}}", - "subresource.document.wizard.button.upload.certificate": "Incarca certificat", - "subresource.document.wizard.label.access.point": "Adresa URL a punctului de acces", - "subresource.document.wizard.label.access.point.mandatory": "Adresa URL a punctului de acces este obligatorie!", - "subresource.document.wizard.label.process.id": "Identificator de proces", - "subresource.document.wizard.label.process.id.mandatory": "Identificatorul de proces este obligatoriu!", - "subresource.document.wizard.label.process.scheme": "Schema proces", - "subresource.document.wizard.label.service.description": "Descriere serviciu", - "subresource.document.wizard.label.technical.contact.url": "Adresa URL de contact tehnic", - "subresource.document.wizard.label.transport.profile": "Profil transport", - "subresource.document.wizard.label.transport.profile.mandatory": "Profilul de transport (de ex: 'bdxr-transport-ebms3-as4-v1p0') este obligatoriu!", - "subresource.document.wizard.label.upload.certificate.mandatory": "Un certificatul x509 valid este obligatoriu!", - "subresource.document.wizard.legend.required.fields": "* campuri obligatorii", - "subresource.document.wizard.placeholder.upload.certificate": "Incarcati un certificat sau inserati valoarea in camp", - "subresource.document.wizard.title": "Expert subresursa", - "subresource.document.wizard.tooltip.access.point": "Adresa unui punct final, ca URL.", - "subresource.document.wizard.tooltip.process.id": "Partea de valoare a identificatorului procesului.", - "subresource.document.wizard.tooltip.process.scheme": "Partea schema a identificatorului procesului.", - "subresource.document.wizard.tooltip.service.description": "Descrierea serviciului", - "subresource.document.wizard.tooltip.technical.contact.url": "Contactul tehnic pentru serviciu", - "subresource.document.wizard.tooltip.transport.profile": "Indica tipul de metoda de transport utilizata intre punctele de acces pentru a schimba mesaje. Consulati specificatiile tehnice ale retelei de schimb de mesaje pentru valorile corecte.", - - "subresource.dialog.button.close": "Inchide", - "subresource.dialog.button.create": "Creeaza", - "subresource.dialog.description": "Pentru a crea o subresursa noua introduceti identificatorul unic si schema dupa care dati click pe Salveaza.", - "subresource.dialog.label.resource.type": "Tip resursa selectata", - "subresource.dialog.label.subresource.id": "Identificator subresursa", - "subresource.dialog.label.subresource.scheme": "Schema subresursa", - "subresource.dialog.placeholder.resource.type": "Tipul selecta al subresursei", - "subresource.dialog.tooltip.resource.type": "Selectati tipul subresursei.", - - "subresource.panel.button.create": "Creaza", - "subresource.panel.button.delete": "Sterge", - "subresource.panel.button.edit": "Modifica", - "subresource.panel.error.delete.resource.data": "Nu se poate sterge subresursa din cauza datelor nevalide despre resursa. Este resursa selectata?", - "subresource.panel.error.delete.subresource.data": "Nu se poate sterge subresursa din cauza datelor nevalide despre subresursa. Este subresursa selectata?", - "subresource.panel.delete.confirmation.dialog.description": "Actiunea va sterge definiti subresursa [{{identifierScheme}}] si identificatorul: [{{identifierValue}}]!<br/><br/>Doriti sa continuati?", - "subresource.panel.delete.confirmation.dialog.title": "Sterge resursa cu schema din DomiSMP", - "subresource.panel.label.filter": "Filtru resursa", - "subresource.panel.label.identifier.scheme": "Schema", - "subresource.panel.label.identifier.value": "Identificator", - "subresource.panel.label.no.filter.results": "Nicio resursa care sa se potriveasca cu filtrul \"{{filterValue}}\"", - "subresource.panel.label.no.data.found": "Resursa nu are subresurse", - "subresource.panel.label.subresource.name": "Modifica documentul subresursei", - "subresource.panel.label.select.pages": "Selectati pagina", - "subresource.panel.subresource.dialog.title": "Creaza Subresursa", - "subresource.panel.placeholder.filter": "Filtru resurse", - "subresource.panel.success.delete": "Subresursa [{{identifierScheme}}] si identificatorul: [{{identifierValue}}] sters.", - "subresource.panel.title": "Subresurse", - "subresource.panel.tooltip.create": "Creaza", - "subresource.panel.tooltip.delete": "Sterge resursa selectata", - "subresource.panel.tooltip.edit": "Modifica", - - "edit.resource.label.filter": "Filtru resurse", - "edit.resource.label.identifier.scheme": "Schema", - "edit.resource.label.identifier.value": "Identificator", - "edit.resource.label.members": "Membri", - "edit.resource.label.no.filter.results": "Nicio resursa care sa se potriveasca cu filtrul \"{{filterValue}}\"", - "edit.resource.label.no.filter.results.admin": "Nicio resursa care sa se potriveasca cu filtrul", - "edit.resource.label.no.data.found": "Grupul nu are resurse", - "edit.resource.label.resource.details": "Detalii resursa", - "edit.resource.label.select.pages": "Selectati pagina", - "edit.resource.label.selected.domain": "Domeniul selectat", - "edit.resource.label.selected.group": "Grupul selectat", - "edit.resource.label.subresources": "Subresursa", - "edit.resource.label.user.not.administrator": "Utilizatorul nu este Administrator al niciunei resurse", - "edit.resource.placeholder.filter": "Filtru resurse", - "edit.resource.placeholder.selected.domain": "Domeniul selectat", - "edit.resource.placeholder.selected.group": "Grupul selectat", - "edit.resource.text": "Panoul de administrare a resurselor este un instrument pentru Administratorii de resurse cu care acestia pot gestiona resurse", - "edit.resource.title": "Modifica resursa", - "edit.resource.tooltip.selected.domain": "Domeniul selectat.", - "edit.resource.tooltip.selected.group": "Grupul selectat.", - - "login.button.login": "Autentifica", - "login.button.password.reset": "Solicitati resetarea parolei", - "login.button.sso.login": "SSO Autentifica", - "login.dialog.password.expiration.dialog.description": "Parola dvs. este pe cale sa expire la {{expirationDate}}! Va rugam sa va schimbati parola inainte de data expirarii!", - "login.dialog.password.expiration.dialog.title": "Avertizare! Parola dvs. este pe cale sa expire", - "login.error.generic.error": "A aparut o eroare generica ({{errorStatus}}) in timpul conectarii.", - "login.error.inactive.user": "Utilizatorul este inactiv. Va rugam sa contactati Administratorul.", - "login.error.invalid.credentials": "{{errorStatus}} Combinatia nume de utilizator/parola pe care ati furnizat-o nu este valida. Incercati din nou sau contactati Administratorul.", - "login.error.invalid.username": "Va rugam sa introduceti un nume de utilizator valid.", - "login.error.invalid.username.or.password": "Va rugam sa introduceti un nume de utilizator si o parola valide.", - "login.error.smp.not.running": "Nu se poate autentifica. SMP nu ruleaza.", - "login.label.login": "Autentificare", - "login.label.logout": "Deconectare", - "login.label.password": "Parola", - "login.label.password.reset": "Solicitati resetarea parolei", - "login.label.sso.login": "Autentificare SSO", - "login.label.login.username": "Nume utilizator", - "login.label.password.reset.username": "Nume utilizator", - "login.title.smp.login": "Autentificare SMP", - "login.title.sso.login": "Autentificare SSO:", - "login.success.confirmation.email.sent": "A fost trimis un e-mail de confirmare la adresa dvs. de e-mail inregistrata pentru utilizatorul [{{userId}}]. Va rugam sa urmati instructiunel din e-mail pentru a finaliza procedul de resetare a contului. Daca nu ati primit acest e-mail, incercati mai tarziu sau contactati Administratorul", - - "resource.search.button.clear.all": "Goleste tot", - "resource.search.error.fetch.resource.metadata": "A aparut o eroare la preluarea metadatelor resurselor", - "resource.search.label.document.type": "Tip document", - "resource.search.label.domain": "Domeniu", - "resource.search.label.no.subresources": "Nicio subresursa", - "resource.search.label.open.url": "Deschide adresa URL", - "resource.search.label.resource.id": "Identificator resursa", - "resource.search.label.resource.scheme": "Schema resursa", - "resource.search.label.column.document.type": "Tip document", - "resource.search.label.column.domain": "Domeniu", - "resource.search.label.column.resource.id": "Identificator resursa", - "resource.search.label.column.resource.scheme": "Schema resursa", - "resource.search.label.column.resource.url": "Adresa URL resursa", - "resource.search.label.column.subresource.count": "Numar Subr.", - "resource.search.label.subresource.id": "Identificator Subresursa", - "resource.search.label.subresource.id.scheme": "Schema identificatoro subresursa", - "resource.search.placeholder.document.type": "Tip document", - "resource.search.placeholder.domain": "Domeniu", - "resource.search.title": "Cautare", - "resource.search.tooltip.document.type": "Tip document", - "resource.search.tooltip.domain": "Domeniu", - - "default.password.dialog.button.ok": "OK", - "default.password.dialog.title": "Folositi parola implicita pentru acest utilizator. Va recomandam sa o schimbati folosind consola utilizatorului.", - - "reset.credentials.button.cancel": "Anuleaza", - "reset.credentials.button.update.password": "Seteaza/schimba parola", - "reset.credentials.error.passwords.mismatch": "Confirmare parolei noi nu se potriveste cu noua parola!", - "reset.credentials.error.old.password.reused": "Noua parola nu trebuie sa fie egala cu vechea parola actuala!", - "reset.credentials.label.username": "Introduceti Nume utilizator", - "reset.credentials.label.new.password": "Parola noua", - "reset.credentials.label.new.password.confirmation": "Confirmati Parola noua", - "reset.credentials.legend.required.fields": "* campuri obligatorii", - "reset.credentials.success.password.reset": "Parola a fost resetata cu succes. Conectati-va din nou la aplicatie folosind noua parola", - - "domain.panel.button.reset": "Reseteaza", - "domain.panel.button.save": "Salveaza", - "domain.panel.label.domain": "Cod de Domeniu [{{value}}]", - "domain.panel.label.domain.already.exists": "Codul de Domeniu exista deja!", - "domain.panel.label.domain.default.resource.type": "Tipul de resursa implicit pentru domeniu", - "domain.panel.label.domain.mandatory": "Codul de domeniu trebuie sa fie constituit doar din caractere si din numere si trebuie sa contina pana la 63 de caractere.", - "domain.panel.label.domain.visibility": "Vizibilitatea domeniului", - "domain.panel.label.signature.cert.alias": "Certificatul semnaturii de raspuns (Semnatura CertAlias)", - "domain.panel.text": "Introduceti datele si dati click pe 'Salveaza' pentru a crea un domeniu nou", - "domain.panel.title": "Detalii Domeniu", - "domain.panel.hint.domain": "Pentru integrarea cu API-ul serviciu web: proprietatea de Domeniu", - "domain.panel.hint.domain.visibility": "Vizibilitatea domeniului. In cazul unui utilizator Intern, acesta trebuie sa fie autentificat pentru a putea citi resursele domeniului", - "domain.panel.hint.signature.cert.alias": "O valoare goala va face ca raspunsurile Resursa sa nu fie semnate de SMP!", - "domain.panel.tooltip.domain": "Codul de domeniu al SMP. Codul trebuie sa fie unic si este folosit in antetul HTTP 'Domain' sau in portiunea de URL atunci cand se preia/creeaza resursa folosind API-ul serviciu web", - "domain.panel.tooltip.domain.default.resource.type": "Tipul de resursa implicit pentru domeniu.", - "domain.panel.tooltip.domain.visibility": "Vizibilitatea domeniului.", - "domain.panel.tooltip.signature.cert.alias": "Certificatul este folosit la semnarea raspunsurilor REST pentru domeniu.", - "domain.panel.warning.domain.configuration.option.admin.member": "<li>adaugati un membru de domeniu cu rolul 'ADMIN' din fila Membri!</li>", - "domain.panel.warning.domain.configuration.option.resource.type": "<li>selectati cel putin un tip de resursa din fila Tipuri de Resursa</li>", - "domain.panel.warning.domain.configuration.option.signature.key": "<li>selectati cheia care va fi folosita pentru semnarea raspunsurilor domeniului!</li>", - "domain.panel.warning.domain.configuration.prefix": "Pentru a finaliza configurarea domeniului, va rugam: <ul>", - - "domain.properties.button.reset": "Reseteaza", - "domain.properties.button.save": "Salveaza", - "domain.properties.label.domain.property": "Proprietatea Domeniului", - "domain.properties.label.domain.value": "Valoarea Domeniului", - "domain.properties.label.system.default": "Implicita Sistem", - "domain.properties.label.no.properties.found": "Nu s-au gasit proprietati ale Domeniului", - "domain.properties.panel.title": "Proprietati ale Domeniului", - - "domain.resource.type.panel.button.reset": "Reseteaza", - "domain.resource.type.panel.button.save": "Salveaza", - "domain.resource.type.label.domain.resource.type.configuration": "Configurarea tipului resursei de domeniu", - - "domain.sml.integration.panel.button.register": "Inregistreaza", - "domain.sml.integration.panel.button.reset": "Reseteaza", - "domain.sml.integration.panel.button.save": "Salveaza", - "domain.sml.integration.panel.button.unregister": "Anulati inregistrarea", - "domain.sml.integration.panel.hint.sml.domain": "Componenta specifica domeniului a zonei SML DNS (de ex. 'mydomain' in cazul 'mydomain.sml.dns.zone').", - "domain.sml.integration.panel.hint.smp.id": "Identificatorul SMP folosit pentru SML", - "domain.sml.integration.panel.label.smp.id": "Identificatorul SML SMP", - "domain.sml.integration.panel.label.smp.id.already.exists": "Identificatorul SML SMP exista deja!", - "domain.sml.integration.panel.label.smp.id.mandatory": "Identificatorul SML SMP trebuie sa contina pana la 63 de caractere, trebuie sa fie constituit doar din caractere alfanumerice si cratime, nu trebuie sa inceapa cu o cifra sau o cratima si nu trebuie sa se termine cu o cratima.", - "domain.sml.integration.panel.label.smp.client.certificate.alias": "Alias-ul Certificatului Client SML", - "domain.sml.integration.panel.label.sml.client.certificate.toggle": "Folositi antetul HTTP 'ClientCert' la autentificare.", - "domain.sml.integration.panel.label.sml.domain": "Domeniu SML", - "domain.sml.integration.panel.label.sml.domain.already.exists": "Domeniul cu subdomeniu gol SML exista deja!", - "domain.sml.integration.panel.label.sml.domain.mandatory": "Domeniul SML trebuie sa contina pana la 63 characters, trebuie sa contina doar caractere alfanumerice si cratime, nu trebuie sa inceapa cu o cifra sau cratima si nu trebuie sa se termine cu o cratima.", - "domain.sml.integration.panel.label.sml.subdomain.already.exists": "Subdomeniul SML este deja definit!", - "domain.sml.integration.panel.register.confirmation.dialog.description": "Actiunea va inregistra domeniul: [{{domainCode}}] si toate resursele acestuia in SML.<br/><br/>Doriti sa continuati?", - "domain.sml.integration.panel.register.confirmation.dialog.title": "Inregistrarea domeniului in SML", - "domain.sml.integration.panel.error.register": "A aparut o eroare la inregistrarea domeniului: {{domainCode}}", - "domain.sml.integration.panel.error.register.unknown.error": "Eroare necunoscuta. Verificati log-urile aplicatiei.", - "domain.sml.integration.panel.error.unregister": "A aparut o eroare la anularea inregistratii domeniului: {{domainCode}}", - "domain.sml.integration.panel.error.unregister.unknown.error": "Eroare necunoscuta. Verificati log-urile aplicatiei.", - "domain.sml.integration.panel.success.register": "Domeniul [{{domainCode}}] inregistrat in SML!", - "domain.sml.integration.panel.success.unregister": "Inregistrarea domeniul [{{domainCode}}] anulata din SML!", - "domain.sml.integration.panel.text": "Domeniul este inregistrat in SML!</p>Domeniul inregistrat nu poate fi sters si nici nu poate sa i se modifice identificatorul SMP SML", - "domain.sml.integration.panel.title": "Datele de integrare SML", - "domain.sml.integration.panel.tooltip.sml.domain": "Componenta specifica domeniului a zonei SML DNS (de ex. 'mydomain' in cazul 'mydomain.sml.dns.zone' sau lasati necompletat pentru valoare implicita a sml.dns.zone). Nota: are doar valoare informativa, zona SML DNS utilizata pentru publicare se bazeaza pe configuratia SML.", - "domain.sml.integration.panel.tooltip.smp.client.certificate.alias": "Certificatul Client folosit pentru autentificarea SML. Antetul HTTP 'Client-Cert' al SML este generat de asemenea din certificat", - "domain.sml.integration.panel.tooltip.smp.client.certificate.alias.default.option": "Alegeti certificatul pentru semnarea raspunsurilor SOAP", - "domain.sml.integration.panel.unregister.confirmation.dialog.description": "Actiunea va anula inregistrarea domeniului: [{{domainCode}}] si a tuturor resurselor acestuia din SML.<br/><br/>Doriti sa continuati?", - "domain.sml.integration.panel.unregister.confirmation.dialog.title": "Anulari Inregistrarea Domeniului din SML", - - "admin.domain.button.create": "Creeaza Domeniu", - "admin.domain.button.delete": "Sterge Selectia", - "admin.domain.label.configuration": "Configurare", - "admin.domain.label.domain.data": "Date de domeniu", - "admin.domain.label.domain.code": "Codul de domeniu", - "admin.domain.label.filter": "Filtru coduri de domenii", - "admin.domain.label.no.filter.results": "Nu exista date care sa se potriveasca cu filtrul {{filterValue}}", - "admin.domain.label.no.data.found": "Nu exista date", - "admin.domain.label.members": "Membrii", - "admin.domain.label.select.page": "Selectati pagina", - "admin.domain.label.no.domains.selected": "Niciun domeniu selectat.", - "admin.domain.label.resource.type": "Tipul Resursei", - "admin.domain.label.sml.integration": "Integrare SML", - "admin.domain.error": "Eroare: {{actionMessage}}", - "admin.domain.success.create": "Domeniul: [{{domainCode}}] a fost creat!", - "admin.domain.success.remove": "Domeniul: [{{domainCode}}] este eliminat!", - "admin.domain.text": "Panoul de administrare a domeniilor este un instrument cu care se pot crea si sterge domenii in DomiSMP", - "admin.domain.title": "Administrarea Domeniilor", - - "extension.panel.extension.description": "Descriere", - "extension.panel.label.description": "Descriere", - "extension.panel.label.extension.name": "Numele extensiei", - "extension.panel.label.extension.version": "Versiunea extensiei", - "extension.panel.label.resource.definitions": "Definitiile resursei", - "extension.panel.label.name": "Nume", - "extension.panel.label.id": "Identificator", - "extension.panel.label.url.segment": "Segment adresa URL", - "extension.panel.label.mime.type": "Tipul MIME", - "extension.panel.label.select.page": "Selectati pagina", - "extension.panel.label.show.selected.resources": "Afisati resursa selectata", - "extension.panel.placeholder.extension.name": "Numele extensiei", - "extension.panel.placeholder.extension.version": "Versiunea", - "extension.panel.title": "Datele Extensiei", - - "resource.details.dialog.button.cancel": "Anuleaza", - "resource.details.dialog.resource.description": "Selectati datele de definire a resursei", - "resource.details.dialog.subresources.description": "Definitia Subresursei", - "resource.details.dialog.extension.description": "Descriere", - "resource.details.dialog.label.name": "Numele", - "resource.details.dialog.label.id": "Identificator", - "resource.details.dialog.label.url.segment": "Segment adresa URL", - "resource.details.dialog.label.mime.type": "Tipul MIME", - "resource.details.dialog.label.description": "Descriere", - "resource.details.dialog.label.resource.id": "Identificator resursa", - "resource.details.dialog.label.resource.name": "Numele resursei", - "resource.details.dialog.label.resource.mime.type": "Tipul MIME al resursei", - "resource.details.dialog.label.select.page": "Selectati pagina", - "resource.details.dialog.label.url": "Segment adreasa URL/antet HTTP", - "resource.details.dialog.placeholder.resource.id": "Identificator resursa", - "resource.details.dialog.placeholder.resource.name": "Nume resursa", - "resource.details.dialog.placeholder.resource.mime.type": "Tip MIME resursa", - "resource.details.dialog.placeholder.url": "Segment adreasa URL/antet HTTP", - "resource.details.dialog.title": "Detalii de Definire ale Resursei", - - "extensions.label.filter": "Filtru nume extensii", - "extensions.label.no.filter.results": "Nu exista date care sa se potriveasca cu filtrul \"{{filterValue}}\"", - "extensions.label.no.data.found": "Nu exista date", - "extensions.label.name": "Nume", - "extensions.label.version": "Versiune", - "extensions.label.select.page": "Selectati pagina", - "extensions.label.no.extension.selected": "Nicio extensie selectata.", - "extensions.text": "Extensii inregistrate in DomiSMP.<br />DomiSMP accepta tipuri de documente prin extensie personalizata. Extensiile implementeaza instrumente pentru validarea si generarea resurselor si subresurselor.<br /> Extensiile pot implementa, de asemenea, logica personalizata pentru scanarea atasamentelor (de ex. detectii de virusi).", - "extensions.title": "Extensii", - - "keystore.import.dialog.button.import": "Importa", - "keystore.import.dialog.button.cancel": "Anuleaza", - "keystore.import.dialog.error.import": "A aparut o eroare la importul keystore: {{fileName}}", - "keystore.import.dialog.error.upload": "A aparut o eroare la incarcarea fisierului keystore {{fileName}}", - "keystore.import.dialog.error.generic": "A aparut o eroare la citirea keystore.", - "keystore.import.dialog.error.generic.message": "Verificati daca fisierul incarcat este un tip de keystore valid.", - "keystore.import.dialog.hint.password": "Cheile si keystore-ul trebuie sa utilizeze aceeasi parola!", - "keystore.import.dialog.label.choose": "Alegeti keystore-ul", - "keystore.import.dialog.label.keystore.file.name": "Alegeti un fisier - keystore!", - "keystore.import.dialog.label.keystore.type.mandatory": "Keystore type is required!", - "keystore.import.dialog.label.password.mandatory": "Parola este obligatorie! (Cheile si keystore-ul trebuie sa utilizeze aceeasi parola!)", - "keystore.import.dialog.legend.required.fields": "* campuri obligatorii", - "keystore.import.dialog.placeholder.keystore.file.name": "Numele fisierului keystore", - "keystore.import.dialog.placeholder.keystore.type": "Tipul keystore-ului", - "keystore.import.dialog.placeholder.keystore.type.jks": "Keystore Java (JKS)", - "keystore.import.dialog.placeholder.keystore.type.pkcs12": "PKCS #12 (PKCS12)", - "keystore.import.dialog.placeholder.password": "Parola", - "keystore.import.dialog.title": "Importare Keystore", - "keystore.import.dialog.warning.ignored.aliases": "Urmatoarele alias-uri au fost ignorate deoarece erau deja prezente in keystore-ul curent: {{ignoredAliases}}", - - "admin.keystore.button.import.keystore": "Importa keystore", - "admin.keystore.button.delete.key": "Sterge cheia selectata", - "admin.keystore.label.alias": "Alias", - "admin.keystore.label.certificate": "Certificat", - "admin.keystore.label.key.pair": "Pereche de chei", - "admin.keystore.label.type": "Tip", - "admin.keystore.label.no.certificate.selected": "Niciun certificat selectat.", - "admin.keystore.label.invalid.certificate": "Certificat nevalid: {{reason}}", - "admin.keystore.label.filter": "Filtru alias-uri certificate/chei", - "admin.keystore.label.no.filter.results": "Nu exista date care sa se potriveasca cu filtrul \"{{filterValue}}\"", - "admin.keystore.label.no.data.found": "Nu exista date", - "admin.keystore.label.select.page": "Selectati pagina", - "admin.keystore.delete.confirmation.dialog.description": "Actiunea va sterge definitiv cheia din keystore!<br/><br/>Doriti sa continuati?", - "admin.keystore.delete.confirmation.dialog.title": "Sterge cheia [{{alias}}] din keystore", - "admin.keystore.success.certificates.added": "Certificate adaugate [{{aliases}}]", - "admin.keystore.success.certificates.deleted": "Certificate sterse [{{aliases}}]", - "admin.keystore.success.errors.detected": "Erori detectate [{{errors}}]", - "admin.keystore.placeholder.filter": "alias", - "admin.keystore.text": "Keystore-ul contine chei pentru semnarea raspunsurilor si chei client pentru integrarea cu SML.", - "admin.keystore.title": "Administrare Keystore", - - "property.label.column.property": "Proprietate", - "property.label.column.value": "Valoare", - "property.label.column.title.property": "Cheia de proprietate.", - "property.label.column.title.value": "Valoare proprietatii.", - "property.label.filter": "Filtru nume proprietati", - "property.label.new.value": "Noua valoare: '{{value}}'.", - "property.label.server.restart.needed": "Este necesara repornirea serverului!", - "property.label.server.restart.time": "Timp de actualizare programat la: {{date}}", - "property.title": "Proprietati", - "property.tooltip.filter": "Filtru dupa numele proprietatilor", - - "admin.truststore.button.add.certificate": "Adauga certificat", - "admin.truststore.button.delete.certificate": "Sterge selectia", - "admin.truststore.error": "Eroare: {{actionMessage}}", - "admin.truststore.delete.confirmation.dialog.description": "Actiunea va sterge definitiv certificatul din truststore!<br/><br/>Doriti sa continuati?", - "admin.truststore.delete.confirmation.dialog.title": "Sterge certificatul {{alias}} din truststore", - "admin.truststore.label.alias": "Alias", - "admin.truststore.label.certificate.trustiness": "Cand truststore este gol, increderea in certificat nu este validata!", - "admin.truststore.label.filter": "Filtru alias-uri certificate", - "admin.truststore.label.invalid.certificate": "Certificat nevalid: {{reason}}", - "admin.truststore.label.no.filter.results": "Nu exista date care sa se potriveasca cu filtrul \"{{filterValue}}\"", - "admin.truststore.label.no.data.found": "Nu exista date", - "admin.truststore.label.no.certificates.selected": "Niciun certificat selectat.", - "admin.truststore.label.select.page": "Selectati pagina", - "admin.truststore.placeholder.filter": "Alias", - "admin.truststore.success.import": "Certificatul: [{{certificateId}}] avand alias [{{alias}}] este importat!", - "admin.truststore.success.remove": "Certificatul: [{{certificateId}}] avand alias [{{alias}}] este sters!", - "admin.truststore.text": "Truststore contine certificatele in care exista incredere.<br />Certificatele folosite pentru autentificare trebuie sa fie continute in truststore.", - "admin.truststore.title": "Administrare Truststore", - - "admin.user.button.create": "Creaza utilizator", - "admin.user.button.delete": "Sterge selectia", - "admin.user.delete.confirmation.dialog.description": "Actiunea va sterge definitiv utilizatorul!<br/><br/>Doriti sa continuati?", - "admin.user.delete.confirmation.dialog.title": "Sterge utilizatorul {{username}} din DomiSMP", - "admin.user.label.filter": "Filtru utilizatori", - "admin.user.label.full.name": "Nume complet", - "admin.user.label.username": "Nume utilizator", - "admin.user.label.no.filter.results": "Nu exista utilizatori care sa se potriveasca cu filtrul \"{{filterValue}}\"", - "admin.user.label.no.data.found": "Nu exista date", - "admin.user.label.select.page": "Selectati pagina", - "admin.user.label.no.user.selected": "Niciun utilizator selectat.", - "admin.user.placeholder.filter": "Nume utilizator sau nume complet", - "admin.user.success.create": "Utilizatorul [{{username}}] a fost creat!", - "admin.user.success.delete": "Utilizatorul [{{username}}] a fost sters!", - "admin.user.success.password.updated": "Parola utilizatorului actualizata!", - "admin.user.success.update": "Utilizatorul [{{username}}] actualizat!", - "admin.user.text": "Panoul de administrarea a utilizatorilor este un instrument cu care se pot crea si sterge utilizatori din DomiSMP", - "admin.user.title": "Administrarea Utilizatorilor", - - "dns.query.panel.label.dns.query": "Interogarea DNS nu a functionat", - "dns.query.panel.label.resolved.dns.entries": "Intrari DNS rezolvate", - "dns.query.panel.title": "Domeniu DNS", - - "dns.tools.button.generate.lookup.query": "Genereaza interogare", - "dns.tools.label.resource.id": "Identificator resursa", - "dns.tools.label.resource.scheme": "Schema resursa", - "dns.tools.label.resource.top.domain": "Domeniu de top", - "dns.tools.description": "Introduceti datele DNS-ului pentru a crea o interogare DNS", - "dns.tools.title": "Interogare DNS", - - "access.token.panel.button.delete": "Sterge", - "access.token.panel.button.save": "Salveaza", - "access.token.panel.error.invalid.end.date": "Data expira la nevalida", - "access.token.panel.error.invalid.start.date": "Data activ de la nevalida", - "access.token.panel.label.active": "Activ", - "access.token.panel.label.description": "Descriere", - "access.token.panel.label.login.failed.attempts": "Incercari esuate", - "access.token.panel.label.login.last.failed.attempt": "Ultima incercare esuata", - "access.token.panel.label.token.expired": "Token-ul de acces a expirat", - "access.token.panel.label.suspended.until": "Dezactivat pana la", - "access.token.panel.label.validity.dates": "Introduceti un interval valid de date", - "access.token.panel.placeholder.end.date": "Data de incheiere", - "access.token.panel.placeholder.start.date": "Data de inceput", - - "user.access.tokens.button.create": "Creaza Token de acces", - "user.access.tokens.label.access.tokens": "Token-uri de access", - "user.access.tokens.label.select.page": "Selectati pagina", - "user.access.tokens.text": "Puteti genera un Token de acces pentru fiecare aplicatie care are nevoie de acces la API-ul DomiSMP.", - "user.access.tokens.title": "Token de Access", - "user.access.tokens.credentials.dialog.title": "Nou Token de access creat", - "user.access.tokens.delete.confirmation.dialog.description": "Actiunea va sterge Token-ul de acces: \"{{credentialName}}\"!<br /><br />Doriti sa continuati?", - "user.access.tokens.delete.confirmation.dialog.title": "Sterge Token-ul de access", - "user.access.tokens.update.confirmation.dialog.description": "Actiunea va actualiza Token-ul de acces: \"{{credentialName}}\"!<br /><br />Doriti sa continuati?", - "user.access.tokens.update.confirmation.dialog.title": "Actualizeaza Token-ul de acces", - "user.access.tokens.tooltip.create": "Creaza Token de access nou", - - "user.certificate.panel.button.delete": "Sterge", - "user.certificate.panel.button.show.details": "Arata detaliile", - "user.certificate.panel.button.save": "Salveaza", - "user.certificate.panel.label.active": "Activ", - "user.certificate.panel.label.certificate.id": "Identificator certificat", - "user.certificate.panel.label.description": "Descriere", - "user.certificate.panel.label.invalid.end.date": "Data expira la nevalida", - "user.certificate.panel.label.invalid.certificate": "Certificate nevalid: {{reason}}", - "user.certificate.panel.label.invalid.start.date": "Data activ de la nevalida", - "user.certificate.panel.label.validity.dates": "Perioada de valabilitate a certificatului", - "user.certificate.panel.placeholder.end.date": "Data de incheiere", - "user.certificate.panel.placeholder.start.date": "Data de inceput", - - "user.certificates.button.import": "Importa certificat nou", - "user.certificates.label.user.certificate": "Certificate utilizatori", - "user.certificates.label.select.page": "Selectati pagina", - "user.certificates.credentials.dialog.title": "Importa Certificat", - "user.certificates.delete.confirmation.dialog.description": "Actiunea va sterge certificatul: \"{{credentialName}}\"!<br /><br />Doriti sa continuati?", - "user.certificates.delete.confirmation.dialog.title": "Sterge Certificat", - "user.certificates.update.confirmation.dialog.description": "Actiunea va actualiza datele certificatului:<br />{{credentialName}}!<br /><br />Doriti sa continuati?", - "user.certificates.update.confirmation.dialog.title": "Actualizeaza Certificat", - "user.certificates.text": "Inregistreaza un certificat de utilizator X509 pentru a avea acces la API-ul DomiSMP API utilizand autentificarea TLS reciproca.", - "user.certificates.title": "Certificate de utilizator X509", - "user.certificates.tooltip.import": "Importa certificat nou", - - "toolbar.component.button.change.password": "Modifica parola", - "toolbar.component.button.logout": "Deconectare", - "toolbar.component.button.not.logged.in": "Neconectat", - "toolbar.component.button.open.cas.profile": "Deschideti datele CAS ale utilizatorului", - "toolbar.component.label.system.administrator.role": "Administrator de sistem", - "toolbar.component.label.user.role": "Utilizator SMP", - - "app.component.button.collapse": "Restrangeti bara laterala", - "app.component.tooltip.collapse": "Retrangeti", - "app.component.tooltip.expand": "Extindeti", - - "navigation.label.home": "Pagina de start", - "navigation.label.search": "Cautare", - "navigation.label.search.resources": "Resurse", - "navigation.label.search.dns.tools": "Instrumente DNS", - "navigation.label.login": "Authentificare", - "navigation.label.reset.token": "Resetare Token", - "navigation.label.edit": "Administrare", - "navigation.label.edit.domains": "Editare Domenii", - "navigation.label.edit.groups": "Editare Grupuri", - "navigation.label.edit.resources": "Editare Resurse", - "navigation.label.edit.resource.document": "Editare Documentul Resursei", - "navigation.label.edit.subresource.document": "Editare Documentul Subresursei", - "navigation.label.system.settings": "Setari de Sistem", - "navigation.label.system.settings.alerts": "Alerte", - "navigation.label.system.settings.domains": "Domenii", - "navigation.label.system.settings.extensions": "Extensii", - "navigation.label.system.settings.keystores": "Keystore", - "navigation.label.system.settings.properties": "Proprietati", - "navigation.label.system.settings.truststores": "Truststore", - "navigation.label.system.settings.users": "Utilizatori", - "navigation.label.user.settings": "Setari Utilizator", - "navigation.label.user.settings.profile": "Profil", - "navigation.label.user.settings.access.tokens": "Token-uri de Acces", - "navigation.label.user.settings.certificates": "Certificate", - "navigation.label.user.settings.alerts": "Alertele mele", - "navigation.label.user.settings.membership": "Calitate de Membru", - "navigation.tooltip.search.resources": "Cauta resurse inregistrate", - "navigation.tooltip.search.tools": "Cauta resurse inregistrate", - "navigation.tooltip.search.dns.tools": "Instrumente de cautare DNS" -} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/config/enums/SMPPropertyEnum.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/config/enums/SMPPropertyEnum.java index 931c13213..767c619dc 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/config/enums/SMPPropertyEnum.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/config/enums/SMPPropertyEnum.java @@ -314,6 +314,31 @@ public enum SMPPropertyEnum { OPTIONAL, NOT_ENCRYPTED, NO_RESTART_NEEDED, STRING, "^(.{0,255})$", "Subject must have less than 256 character"), + + ALERT_USER_CREATED_ENABLED("smp.alert.user.created.enabled", + "true", "Enable/disable the user creation alert", + OPTIONAL, NOT_ENCRYPTED, NO_RESTART_NEEDED, BOOLEAN), + ALERT_USER_CREATED_LEVEL("smp.alert.user.created.level", + "HIGH", "User creation alert level. Values: {LOW, MEDIUM, HIGH}", + OPTIONAL, NOT_ENCRYPTED, NO_RESTART_NEEDED, STRING, + "^(LOW|MEDIUM|HIGH)$", "Allowed values are: LOW, MEDIUM, HIGH"), + ALERT_USER_CREATED_MAIL_SUBJECT("smp.alert.user.created.mail.subject", + "New DomiSMP User created", "User creation mail subject.", + OPTIONAL, NOT_ENCRYPTED, NO_RESTART_NEEDED, STRING, + "^(.{0,255})$", "Subject must have less than 256 character"), + + ALERT_USER_UPDATED_ENABLED("smp.alert.user.updated.enabled", + "true", "Enable/disable the user creation alert", + OPTIONAL, NOT_ENCRYPTED, NO_RESTART_NEEDED, BOOLEAN), + ALERT_USER_UPDATED_LEVEL("smp.alert.user.updated.level", + "HIGH", "User creation alert level. Values: {LOW, MEDIUM, HIGH}", + OPTIONAL, NOT_ENCRYPTED, NO_RESTART_NEEDED, STRING, + "^(LOW|MEDIUM|HIGH)$", "Allowed values are: LOW, MEDIUM, HIGH"), + ALERT_USER_UPDATED_MAIL_SUBJECT("smp.alert.user.updated.mail.subject", + "DomiSMP User was updated by administrator", "User update mail subject.", + OPTIONAL, NOT_ENCRYPTED, NO_RESTART_NEEDED, STRING, + "^(.{0,255})$", "Subject must have less than 256 character"), + ALERT_ACCESS_TOKEN_BEFORE_EXPIRATION_ENABLED("smp.alert.accessToken.imminent_expiration.enabled", "true", "Enable/disable the imminent accessToken expiration alert", OPTIONAL, NOT_ENCRYPTED, NO_RESTART_NEEDED, BOOLEAN), diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/config/init/SMPLocaleFileSystemInitializer.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/config/init/SMPLocaleFileSystemInitializer.java index 418096284..cbedd2b57 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/config/init/SMPLocaleFileSystemInitializer.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/config/init/SMPLocaleFileSystemInitializer.java @@ -2,8 +2,7 @@ package eu.europa.ec.edelivery.smp.config.init; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; -import eu.europa.ec.edelivery.smp.services.ConfigurationService; -import eu.europa.ec.edelivery.smp.services.SMPLocaleService; +import eu.europa.ec.edelivery.smp.services.SMPLanguageResourceService; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @@ -14,16 +13,15 @@ import javax.annotation.PostConstruct; * * @since 5.1 * @author Sebastian-Ion TINCU - * @see eu.europa.ec.edelivery.smp.i18n.SMPLocale */ @Component public class SMPLocaleFileSystemInitializer { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(SMPLocaleFileSystemInitializer.class); - private final SMPLocaleService smpLocaleService; + private final SMPLanguageResourceService smpLocaleService; - public SMPLocaleFileSystemInitializer(SMPLocaleService smpLocaleService) { + public SMPLocaleFileSystemInitializer(SMPLanguageResourceService smpLocaleService) { this.smpLocaleService = smpLocaleService; } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/i18n/SMPLocale.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/i18n/SMPLocale.java deleted file mode 100644 index 1502f16be..000000000 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/i18n/SMPLocale.java +++ /dev/null @@ -1,47 +0,0 @@ -package eu.europa.ec.edelivery.smp.i18n; - -import org.apache.commons.lang3.StringUtils; - -import java.util.EnumSet; - -/** - * Locale enumeration for which DomiSMP is providing existing translations. - * - * @since 5.1 - * @author Sebastian-Ion TINCU - */ -public enum SMPLocale { - - /** - * The default English locale - */ - EN_US ("en", "English"), - - /** - * The Romanian locale - */ - RO_RO ("ro", "Romanian"); - - private final String code; - private final String language; - - SMPLocale(String code, String language) { - this.code = code; - this.language = language; - } - - public String getCode() { - return code; - } - - public String getLanguage() { - return language; - } - - public static SMPLocale fromCodeDefaultingToEnglish(String code) { - return EnumSet.allOf(SMPLocale.class).stream() - .filter(locale -> StringUtils.equalsIgnoreCase(locale.getCode(), code)) - .findAny() - .orElse(EN_US); - } -} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ConfigurationService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ConfigurationService.java index c21c5153a..5a634afd7 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ConfigurationService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ConfigurationService.java @@ -447,6 +447,36 @@ public class ConfigurationService { return configurationDAO.getCachedPropertyValue(ALERT_USER_LOGIN_FAILURE_MAIL_SUBJECT); } + //----------------------- + // user Created + public Boolean getAlertUserCreatedEnabled() { + return configurationDAO.getCachedPropertyValue(ALERT_USER_CREATED_ENABLED); + } + + public AlertLevelEnum getAlertUserCreatedLevel() { + String level = configurationDAO.getCachedPropertyValue(ALERT_USER_CREATED_LEVEL); + return AlertLevelEnum.valueOf(level); + } + + public String getAlertUserCreatedSubject() { + return configurationDAO.getCachedPropertyValue(ALERT_USER_CREATED_MAIL_SUBJECT); + } + + //----------------------- + // user updated + public Boolean getAlertUserUpdatedEnabled() { + return configurationDAO.getCachedPropertyValue(ALERT_USER_UPDATED_ENABLED); + } + + public AlertLevelEnum getAlertUserUpdatedLevel() { + String level = configurationDAO.getCachedPropertyValue(ALERT_USER_UPDATED_LEVEL); + return AlertLevelEnum.valueOf(level); + } + + public String getAlertUserUpdatedSubject() { + return configurationDAO.getCachedPropertyValue(ALERT_USER_UPDATED_MAIL_SUBJECT); + } + //----------------------- // user suspended public Boolean getAlertUserSuspendedEnabled() { diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialsAlertService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialsAlertService.java index bf3ec4e85..82dbb170d 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialsAlertService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialsAlertService.java @@ -311,9 +311,14 @@ public class CredentialsAlertService { */ public void alertUserCreated(DBUser user) { + boolean userCreatedAlertEnabled = configurationService.getAlertUserCreatedEnabled(); + if (!userCreatedAlertEnabled) { + LOG.debug("Suppress alert: Alert user created is disabled!"); + return; + } String mailTo = user.getEmailAddress(); - String mailSubject = "New user created"; - AlertLevelEnum alertLevel = AlertLevelEnum.HIGH; + String mailSubject = configurationService.getAlertUserCreatedSubject(); + AlertLevelEnum alertLevel = configurationService.getAlertUserCreatedLevel(); AlertTypeEnum alertType = AlertTypeEnum.USER_CREATED; DBAlert alert = createAlert(user.getUsername(), mailSubject, mailTo, alertLevel, alertType); @@ -335,9 +340,14 @@ public class CredentialsAlertService { */ public void alertUserUpdated(DBUser user) { + boolean userCreatedAlertEnabled = configurationService.getAlertUserUpdatedEnabled(); + if (!userCreatedAlertEnabled) { + LOG.debug("Suppress alert: Alert user updated is disabled!"); + return; + } String mailTo = user.getEmailAddress(); - String mailSubject = "User data updated"; - AlertLevelEnum alertLevel = AlertLevelEnum.HIGH; + String mailSubject = configurationService.getAlertUserUpdatedSubject(); + AlertLevelEnum alertLevel = configurationService.getAlertUserUpdatedLevel(); AlertTypeEnum alertType = AlertTypeEnum.USER_UPDATED; DBAlert alert = createAlert(user.getUsername(), mailSubject, mailTo, alertLevel, alertType); diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/SMPLanguageResourceService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/SMPLanguageResourceService.java new file mode 100644 index 000000000..cba2400f1 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/SMPLanguageResourceService.java @@ -0,0 +1,286 @@ +package eu.europa.ec.edelivery.smp.services; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import eu.europa.ec.edelivery.smp.config.enums.SMPEnvPropertyEnum; +import eu.europa.ec.edelivery.smp.logging.SMPLogger; +import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.stereotype.Service; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Properties; +import java.util.TimeZone; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.apache.commons.lang3.StringUtils.lowerCase; +import static org.apache.commons.lang3.StringUtils.trimToEmpty; + +/** + * Service providing operations for managing language resources (e.g. updating language files on the disk). + * + * @author Sebastian-Ion TINCU + * @since 5.1 + */ +@Service +public class SMPLanguageResourceService { + + public static final String LANGUAGE_FILENAME_UI_PREFIX = "ui_"; + public static final String LANGUAGE_RESOURCE_UI_FOLDER = "/META-INF/resources/ui/assets/i18n/"; + public static final String LANGUAGE_RESOURCE_UI_DEFAULT = LANGUAGE_RESOURCE_UI_FOLDER + "en.json"; + + public static final String LANGUAGE_FILENAME_MAIL_PREFIX = "mail-messages_"; + public static final String LANGUAGE_RESOURCE_MAIL_FOLDER = "/mail-messages/"; + public static final String LANGUAGE_RESOURCE_MAIL_DEFAULT = LANGUAGE_RESOURCE_MAIL_FOLDER + "en.json"; + + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(SMPLanguageResourceService.class); + + private final ConfigurationService configurationService; + private final ResourcePatternResolver resourceResolver; + + public SMPLanguageResourceService(ConfigurationService configurationService, + ResourcePatternResolver resourceResolver) { + this.configurationService = configurationService; + this.resourceResolver = resourceResolver; + } + + + /** + * Method for getting the language file for a specific ISO 639 language code. + * + * @param langCode the ISO 639 language code (e.g. "en" for English, "fr" for French, + * "de" for German, etc.) or null for the default language + * @param filenamePrefix the prefix of the language file (e.g. "ui_" for "ui_en.json") + * @return the path to the language file + */ + public Path getLanguageFile(String filenamePrefix, String langCode) { + File localeFolder = configurationService.getLocaleFolder(); + String languageFileName = getLanguageFilename(filenamePrefix, langCode); + + if (localeFolder != null && !localeFolder.exists() && !localeFolder.mkdirs()) { + LOG.error("Failed to create locale folder [{}]", localeFolder); + return null; + } + return new File(localeFolder, languageFileName).toPath().toAbsolutePath(); + } + + @Cacheable("mail-templates-translations") + public Properties getMailProperties(String langCode) { + Resource langRes = getTranslationResourceFile(LANGUAGE_FILENAME_MAIL_PREFIX, langCode, LANGUAGE_RESOURCE_MAIL_DEFAULT); + ObjectMapper mapper = jsonObjectMapper(); + try (InputStream target = langRes.getInputStream()) { + // Read JSON nodes from input streams + JsonNode jsonTranslation = mapper.readTree(target); + Properties properties = new Properties(); + jsonTranslation.fieldNames().forEachRemaining(fieldName -> + properties.setProperty(fieldName, jsonTranslation.get(fieldName).asText()); + ); + return properties; + } catch (IOException e) { + LOG.error("Error occurred while merging the translation files", e); + return new Properties(); + } + } + + public Resource getTranslationResourceFile(String prefix, String code, String defaultResourceFile) { + + Path langResourcePath = getLanguageFile(prefix, code); + if (langResourcePath != null && langResourcePath.toFile().exists()) { + LOG.debug("Returning local mail translation file [{}]", langResourcePath.toAbsolutePath()); + return new FileSystemResource(langResourcePath); + } else { + LOG.warn("Local translation file [{}] does not exist. Return default translation [{}]!", code, defaultResourceFile); + ClassPathResource defResource = new ClassPathResource(defaultResourceFile); + if (defResource.exists()) { + return defResource; + } else { + LOG.error("Default locale file [{}] does not exist in classpath!", defaultResourceFile); + return null; + } + } + } + + /** + * Method for updating the language files on the disk. The method will copy the language files from the classpath + * to the locale folder. If the locale folder does not exist, the method will try to create it. + * The method will not overwrite existing properties in the translation files. If a property is missing in the + * existing translation file, the method will add it from the classpath translation file. + */ + public void updateLocalesOnDisk() { + updateLocalesOnDisk(LANGUAGE_FILENAME_UI_PREFIX, LANGUAGE_RESOURCE_UI_FOLDER + "*.json"); + updateLocalesOnDisk(LANGUAGE_FILENAME_MAIL_PREFIX, LANGUAGE_RESOURCE_MAIL_FOLDER + "*.json"); + } + + public void updateLocalesOnDisk(String filenamePrefix, String resourcePathPattern) { + // Get all the language files from the classpath + Resource[] resources; + try { + resources = resourceResolver.getResources("classpath:" + resourcePathPattern); + } catch (IOException e) { + LOG.error("An error occurred while reading the language files from the classpath", e); + return; + } + + // Copy the language files to the locale folder + for (Resource resource : resources) { + String filename = resource.getFilename(); + if (filename == null) { + LOG.warn("Resource [{}] does not have a filename!", resource); + continue; + } + String langCode = filename.substring(0, filename.indexOf('.')); + Path localeFile = getLanguageFile(filenamePrefix, langCode); + if (localeFile == null) { + LOG.warn("Can not generate 'locale/language' file [{}]! Check if local folder defined in property [{}] " + + "has writing permissions and the folder exists", localeFile, SMPEnvPropertyEnum.LOCALE_FOLDER.getProperty()); + continue; + } + copyOrUpdateTranslation(localeFile, resource); + } + } + + /** + * Method for copying a resource to a locale folder. If the IOException occurs, the method will silently log an error. + * The error can be caused by the lack of writing permissions or the absence of the locale folder or if the file already exists. + * + * @param resource the resource to copy + * @param localeFile the path to the locale file + */ + protected static void copyResourceToLocaleFolder(Resource resource, Path localeFile) { + try (InputStream inputStream = resource.getInputStream()) { + Files.copy(inputStream, localeFile); + } catch (IOException e) { + LOG.error("An error occurred while updating locale file [{}]", localeFile, e); + } + } + + /** + * Method creates the language filename for a specific ISO 639 language code + * and a prefix. If the prefix is null, it will be ignored. The prefix and the language code + * will be normalized (lower case and trimmed). + * + * @param langCode the ISO 639 language code + * @return the filename for the language file + */ + private String getLanguageFilename(String prefix, String langCode) { + return normalize(prefix) + normalize(langCode) + ".json"; + } + + /** + * Method returns normalized token (lower case and trimmed). + * + * @param token token to normalize + * @return normalized token + */ + private String normalize(String token) { + return trimToEmpty(lowerCase(token)); + } + + /** + * The method will merge the existing translation file with the classpath translation file. The merge will add + * missing properties from the classpath translation file into the existing translation file, but it will not + * overwrite existing properties. + * + * @param localFilePath the path to the local translation file + * @param resourceTranslation + */ + public static void copyOrUpdateTranslation(Path localFilePath, Resource resourceTranslation) { + File localFile = localFilePath.toFile(); + LOG.info("Updating translation file [{}]", localFile); + if (!localFile.exists()) { + LOG.debug("The local translation file [{}] does not exist!", localFile); + copyResourceToLocaleFolder(resourceTranslation, localFilePath); + return; + } + // update file + ObjectMapper mapper = jsonObjectMapper(); + JsonNode mergedJson = null; + boolean changed = false; + try (InputStream target = new FileInputStream(localFilePath.toFile()); + InputStream classpathTranslation = resourceTranslation.getInputStream()) { + // Read JSON nodes from input streams + JsonNode jsonReference = mapper.readTree(classpathTranslation); + mergedJson = mapper.readTree(target); + // Merge the JSON nodes + changed = mergeTranslationJson(mergedJson, jsonReference); + } catch (IOException e) { + LOG.error("Error occurred while merging the translation files", e); + return; + } + + if (!changed) { + LOG.info("No changes were made to the translation file [{}]", localFile); + return; + } + + // Write the merged JSON to the output file + try (OutputStream os = new FileOutputStream(localFilePath.toFile())) { + // Write the merged JSON to the output file + mapper.writerWithDefaultPrettyPrinter().writeValue(os, mergedJson); + } catch (IOException e) { + LOG.error("Error occurred while writing the merged translation file", e); + } + } + + /** + * Merge two JSON nodes recursively. Method will add missing properties from + * the reference node into the target node, but it will not overwrite existing + * properties. If any changes were made to the target node, the method returns + * true else false. + * <p> + * The merge is used to add new translations to the existing language + * files. + * + * @param targetNode the main node to be updates + * @param referenceNode the reference node + * @return true if any changes were made to the target node else false. + */ + protected static boolean mergeTranslationJson(JsonNode targetNode, JsonNode referenceNode) { + AtomicBoolean changed = new AtomicBoolean(false); + referenceNode.fieldNames().forEachRemaining(fieldName -> { + JsonNode jsonNode = targetNode.get(fieldName); + // If the node is an object, recursively merge + boolean mergeChanged = changed.get(); + if (jsonNode != null && jsonNode.isObject()) { + mergeChanged |= mergeTranslationJson(jsonNode, referenceNode.get(fieldName)); + } else if (jsonNode == null && targetNode instanceof ObjectNode) { + // add new field + ((ObjectNode) targetNode).set(fieldName, referenceNode.get(fieldName)); + mergeChanged = true; + } + changed.set(mergeChanged); + }); + return changed.get(); + } + + + /** + * Method for creating a new instance of the {@link ObjectMapper} with the default configuration. + * + * @return the new instance of the {@link ObjectMapper} + */ + public static ObjectMapper jsonObjectMapper() { + ObjectMapper objectMapper = JsonMapper.builder() + .findAndAddModules() + .build(); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + dateFormat.setTimeZone(TimeZone.getDefault()); + objectMapper.setDateFormat(dateFormat); + return objectMapper; + } + +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/SMPLocaleService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/SMPLocaleService.java deleted file mode 100644 index 3605bb2c0..000000000 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/SMPLocaleService.java +++ /dev/null @@ -1,71 +0,0 @@ -package eu.europa.ec.edelivery.smp.services; - -import eu.europa.ec.edelivery.smp.config.enums.SMPEnvPropertyEnum; -import eu.europa.ec.edelivery.smp.i18n.SMPLocale; -import eu.europa.ec.edelivery.smp.logging.SMPLogger; -import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.stereotype.Service; - -import java.io.File; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.EnumSet; - -/** - * Service providing operations for managing locales (e.g. updating locales on the disk). - * - * @since 5.1 - * @author Sebastian-Ion TINCU - */ -@Service -public class SMPLocaleService { - - private static final SMPLogger LOG = SMPLoggerFactory.getLogger(SMPLocaleService.class); - - private final ConfigurationService configurationService; - - public SMPLocaleService(ConfigurationService configurationService) { - this.configurationService = configurationService; - } - - public Path getLocaleFile(SMPLocale locale) { - File localeFolder = configurationService.getLocaleFolder(); - if (!localeFolder.exists() && !localeFolder.mkdirs()) { - LOG.error("Failed to create locale folder [{}]", localeFolder); - return null; - } - return new File(localeFolder, locale.getCode() + ".json").toPath().toAbsolutePath(); - } - - public Resource getLocaleResource(SMPLocale locale) { - return new ClassPathResource("META-INF/resources/ui/assets/i18n/" + locale.getCode() + ".json"); - } - - public void updateLocalesOnDisk() { - EnumSet.allOf(SMPLocale.class).forEach(this::updateLocaleOnDisk); - } - - private void updateLocaleOnDisk(SMPLocale locale) { - Resource resource = getLocaleResource(locale); - if (resource.exists()) { - Path localeFile = getLocaleFile(locale); - if (localeFile == null) { - LOG.warn("Can not generate 'locale/language' file [{}]! Check if local folder defined in property [{}] " + - "has writing permissions and the folder exists", localeFile, SMPEnvPropertyEnum.LOCALE_FOLDER.getProperty()); - return; - } - if (Files.exists(localeFile)) { - LOG.warn("Language file [{}] already exists, and it will be replaced with up-to-date translations", localeFile); - } - try(InputStream inputStream = resource.getInputStream()) { - Files.copy(inputStream, localeFile, StandardCopyOption.REPLACE_EXISTING); - } catch (Exception e) { - LOG.error("An error occurred while updating locale file [{}]", localeFile, e); - } - } - } -} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/mail/MailTemplateService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/mail/MailTemplateService.java index c58f815f5..0abf9a4ac 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/mail/MailTemplateService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/mail/MailTemplateService.java @@ -20,11 +20,11 @@ package eu.europa.ec.edelivery.smp.services.mail; import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; +import eu.europa.ec.edelivery.smp.services.SMPLanguageResourceService; import eu.europa.ec.edelivery.smp.utils.StringNamedSubstitutor; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import java.io.IOException; import java.io.InputStream; @@ -39,16 +39,21 @@ import java.util.Properties; * @author Joze Rihtarsic * @since 5.1 */ -@Component +@Service public class MailTemplateService { - private static final String DEFAULT_LANGUAGE = "en"; private static final String MAIL_TEMPLATE = "/mail-messages/mail-template.htm"; - private static final String MAIL_TEMPLATE_CHARSET= "UTF-8"; + private static final String MAIL_TEMPLATE_CHARSET = "UTF-8"; private static final String MAIL_HEADER = "MAIL_HEADER"; private static final String MAIL_FOOTER = "MAIL_FOOTER"; private static final String MAIL_TITLE = "MAIL_TITLE"; private static final String MAIL_CONTENT = "MAIL_CONTENT"; + SMPLanguageResourceService smpLanguageResourceService; + + public MailTemplateService(SMPLanguageResourceService smpLanguageResourceService) { + this.smpLanguageResourceService = smpLanguageResourceService; + } + public String getMailHtmlContent(MailDataModel model) { InputStream templateIS = MailTemplateService.class.getResourceAsStream(MAIL_TEMPLATE); @@ -82,34 +87,8 @@ public class MailTemplateService { public String getMailData(MailDataModel model, String key) { - Properties translations = getMessageTranslations(model.getLanguage()); + Properties translations = smpLanguageResourceService.getMailProperties(model.getLanguage()); String dataTemplate = translations.getProperty(key); return StringUtils.isBlank(dataTemplate) ? dataTemplate : StringNamedSubstitutor.resolve(dataTemplate, model.getModel()); } - - - @Cacheable("mail-templates-translations") - public Properties getMessageTranslations(String language) { - - Properties translations = loadTranslations(language); - if (translations != null) { - return translations; - } - return loadTranslations(DEFAULT_LANGUAGE); - } - - protected Properties loadTranslations(String language) { - String langResource = "/mail-messages/mail-messages_" + language + ".properties"; - InputStream isLanguage = MailTemplateService.class.getResourceAsStream(langResource); - if (isLanguage != null) { - try { - Properties translations = new Properties(); - translations.load(isLanguage); - return translations; - } catch (IOException e) { - throw new SMPRuntimeException(ErrorCode.INTERNAL_ERROR, "Error reading mail template", ExceptionUtils.getRootCauseMessage(e)); - } - } - return new Properties(); - } } diff --git a/smp-server-library/src/main/resources/mail-messages/en.json b/smp-server-library/src/main/resources/mail-messages/en.json new file mode 100644 index 000000000..f5ceef133 --- /dev/null +++ b/smp-server-library/src/main/resources/mail-messages/en.json @@ -0,0 +1,22 @@ +{ + "mail.credential_changed.content" : "You're receiving this e-mail because your credential type: ${CREDENTIAL_TYPE} changed on DomiSMP ${SMP_INSTANCE_NAME}! <p>If you did not update your credential, please inform DomiSMP administrator immediately.</p><br>If you are having trouble accessing your account, reset your password. ", + "mail.credential_changed.title" : "Credential type: ${CREDENTIAL_TYPE} on DomiSMP ${SMP_INSTANCE_NAME} changed!", + "mail.credential_expired.content" : "<p><strong>Credential type:</strong> ${CREDENTIAL_TYPE}</p> <p><strong>Credential identifier:</strong> ${CREDENTIAL_ID}</p> <p><strong>Expiration date-time: </strong> ${EXPIRATION_DATETIME}</p> <p><strong>Reporting date-time:</strong> ${REPORTING_DATETIME}</p> <p><strong>Alert level:</strong> ${ALERT_LEVEL}</p> <p><strong>SMP instance name:</strong> ${SMP_INSTANCE_NAME}</p>.", + "mail.credential_expired.title" : "Credential type: ${CREDENTIAL_TYPE} is expired", + "mail.credential_imminent_expiration.content" : "<p><strong>Credential type:</strong> ${CREDENTIAL_TYPE}</p><p><strong>Credential identifier:</strong> ${CREDENTIAL_ID}</p><p><strong>Expiration date-time: </strong> ${EXPIRATION_DATETIME}</p><p><strong>Reporting date-time:</strong> ${REPORTING_DATETIME}</p><p><strong>Alert level:</strong> ${ALERT_LEVEL}</p><p><strong>SMP instance name:</strong> ${SMP_INSTANCE_NAME}</p>", + "mail.credential_imminent_expiration.title" : "Credential type: ${CREDENTIAL_TYPE} imminent expiration", + "mail.credential_request_reset.content" : "You're receiving this e-mail because you requested a credential type: ${CREDENTIAL_TYPE} reset on DomiSMP ${SMP_INSTANCE_NAME} for your user account. <br/> Please go to the following page and choose a new password: <br/> <br/> <a href=\"${RESET_URL}\">${RESET_URL}</a> <br/> <br/> The link is valid for a short period of time. If the link has expired, please request a new one.", + "mail.credential_request_reset.title" : "Request for reset of the Credential type: ${CREDENTIAL_TYPE} on DomiSMP ${SMP_INSTANCE_NAME}", + "mail.credential_suspended.content" : "<p><strong>Credential type:</strong> ${CREDENTIAL_TYPE}</p> <p><strong>Credential identifier:</strong> ${CREDENTIAL_ID}</p> <p><strong>Failed login attempt count:</strong> ${FAILED_LOGIN_ATTEMPT}</p> <p><strong>Last failed login time:</strong> ${LAST_LOGIN_FAILURE_DATETIME}</p> <p><strong>Suspended util</strong> ${SUSPENDED_UNTIL_DATETIME}</p> <p><strong>Reporting time:</strong> ${REPORTING_DATETIME}</p> <p><strong>Alert level:</strong> ${ALERT_LEVEL}</p> <p><strong>SMP instance name:</strong> ${SMP_INSTANCE_NAME}</p>", + "mail.credential_suspended.title" : "Credential type: ${CREDENTIAL_TYPE} is temporarily suspended", + "mail.credential_verification_failed.content" : "<p><strong>Credential type:</strong> ${CREDENTIAL_TYPE}</p> <p><strong>Credential identifier:</strong> ${CREDENTIAL_ID}</p> <p><strong>Failed login attempt count:</strong> ${FAILED_LOGIN_ATTEMPT}</p> <p><strong>Last failed login time:</strong> ${LAST_LOGIN_FAILURE_DATETIME}</p> <p><strong>Reporting time:</strong> ${REPORTING_DATETIME}</p> <p><strong>Alert level:</strong> ${ALERT_LEVEL}</p> <p><strong>SMP instance name:</strong> ${SMP_INSTANCE_NAME}</p>", + "mail.credential_verification_failed.title" : "Credential type: ${CREDENTIAL_TYPE} verification failed!", + "mail.footer" : "DomiSMP instance: ${SMP_INSTANCE_NAME} (${SERVER_NAME}), ${CURRENT_DATETIME}", + "mail.header" : "eDelivery DomiSMP sample: ${SMP_INSTANCE_NAME}", + "mail.test_mail.content" : "This is a test mail from DomiSMP instance: ${SMP_INSTANCE_NAME} for your user account.", + "mail.test_mail.title" : "Test mail: ${CREDENTIAL_TYPE} on DomiSMP ${SMP_INSTANCE_NAME}", + "mail.user_created.content" : "You're receiving this e-mail because new user was created with :<br/><b>Username:</b>${USERNAME}<br /> <b>Name:</b>${FULL_NAME}<br /> <b>Email Address:</b>${EMAIL}<br /><b>Activated:</b>${ACTIVATED}<br /> Note: If user account is not yet activated please wait for activation mail, before first login. <br/>If you did not request this account, please inform DomiSMP administrator immediately.", + "mail.user_created.title" : "New user created on DomiSMP ${SMP_INSTANCE_NAME}", + "mail.user_updated.content" : "You're receiving this e-mail because user was updated with :<br/><b>Username:</b>${USERNAME}<br /> <b>Name:</b>${FULL_NAME}<br /> <b>Email Address:</b>${EMAIL}<br /><b>Activated:</b>${ACTIVATED}<br /><br/>If you did not request update account, please inform DomiSMP administrator immediately.", + "mail.user_updated.title" : "User update on DomiSMP ${SMP_INSTANCE_NAME}" +} \ No newline at end of file diff --git a/smp-server-library/src/main/resources/mail-messages/mail-messages_en.properties b/smp-server-library/src/main/resources/mail-messages/mail-messages_en.properties deleted file mode 100644 index 900b6e8bc..000000000 --- a/smp-server-library/src/main/resources/mail-messages/mail-messages_en.properties +++ /dev/null @@ -1,99 +0,0 @@ -### -# #START_LICENSE# -# smp-server-library -# %% -# Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP -# %% -# Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent -# versions of the EUPL (the "Licence"); -# You may not use this work except in compliance with the Licence. -# You may obtain a copy of the Licence at: -# -# [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 -# -# Unless required by applicable law or agreed to in writing, software distributed under the Licence is -# distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the Licence for the specific language governing permissions and limitations under the Licence. -# #END_LICENSE# -### -# Email translations. Each email has a header, title, content and footer. The texts are can be HTML formatted. -# The header and footer are common for all emails and should describe instance and purpose of the application. -# The title and content are specific for each email type. - -mail.header=eDelivery DomiSMP sample: ${SMP_INSTANCE_NAME} -mail.footer=DomiSMP instance: ${SMP_INSTANCE_NAME} (${SERVER_NAME}), ${CURRENT_DATETIME} - -# Email texts for password reset -mail.test_mail.title=Test mail: ${CREDENTIAL_TYPE} on DomiSMP ${SMP_INSTANCE_NAME} -mail.test_mail.content=This is a test mail from DomiSMP instance: ${SMP_INSTANCE_NAME} for your user account. - -mail.user_created.title=New user created on DomiSMP ${SMP_INSTANCE_NAME} -mail.user_created.content=You're receiving this e-mail because new user was created with :<br/>\ - <b>Username:</b>${USERNAME}<br /> \ - <b>Name:</b>${FULL_NAME}<br /> \ - <b>Email Address:</b>${EMAIL}<br />\ - <b>Activated:</b>${ACTIVATED}<br /> \ - Note: If user account is not yet activated please wait for activation mail, before first login. \ - <br/>If you did not request this account, please inform DomiSMP administrator immediately. - - -mail.user_updated.title=User update on DomiSMP ${SMP_INSTANCE_NAME} -mail.user_updated.content=You're receiving this e-mail because user was updated with :<br/>\ - <b>Username:</b>${USERNAME}<br /> \ - <b>Name:</b>${FULL_NAME}<br /> \ - <b>Email Address:</b>${EMAIL}<br />\ - <b>Activated:</b>${ACTIVATED}<br />\ - <br/>If you did not request update account, please inform DomiSMP administrator immediately. - -# Email texts for password reset -mail.credential_request_reset.title=Request for reset of the Credential type: ${CREDENTIAL_TYPE} on DomiSMP ${SMP_INSTANCE_NAME} -mail.credential_request_reset.content=You're receiving this e-mail because you requested a credential type: ${CREDENTIAL_TYPE} \ - reset on DomiSMP ${SMP_INSTANCE_NAME} for your user account. <br/> Please go to the following page and choose a new password: \ - <br/> <br/> <a href="${RESET_URL}">${RESET_URL}</a> <br/> <br/> The link is valid for a short period of time. If the \ - link has expired, please request a new one. - - -mail.credential_changed.title=Credential type: ${CREDENTIAL_TYPE} on DomiSMP ${SMP_INSTANCE_NAME} changed! -mail.credential_changed.content=You're receiving this e-mail because your credential type: ${CREDENTIAL_TYPE} changed \ - on DomiSMP ${SMP_INSTANCE_NAME}! <p>If you did not update your credential, please inform DomiSMP administrator \ - immediately.</p><br>If you are having trouble accessing your account, reset your password. - -# Email texts for credential expired -mail.credential_expired.title=Credential type: ${CREDENTIAL_TYPE} is expired -mail.credential_expired.content=<p><strong>Credential type:</strong> ${CREDENTIAL_TYPE}</p> \ - <p><strong>Credential identifier:</strong> ${CREDENTIAL_ID}</p> \ - <p><strong>Expiration date-time: </strong> ${EXPIRATION_DATETIME}</p> \ - <p><strong>Reporting date-time:</strong> ${REPORTING_DATETIME}</p> \ - <p><strong>Alert level:</strong> ${ALERT_LEVEL}</p> \ - <p><strong>SMP instance name:</strong> ${SMP_INSTANCE_NAME}</p>. - -# Email texts for credential imminent expiration} -mail.credential_imminent_expiration.title=Credential type: ${CREDENTIAL_TYPE} imminent expiration -mail.credential_imminent_expiration.content=<p><strong>Credential type:</strong> ${CREDENTIAL_TYPE}</p>\ - <p><strong>Credential identifier:</strong> ${CREDENTIAL_ID}</p>\ - <p><strong>Expiration date-time: </strong> ${EXPIRATION_DATETIME}</p>\ - <p><strong>Reporting date-time:</strong> ${REPORTING_DATETIME}</p>\ - <p><strong>Alert level:</strong> ${ALERT_LEVEL}</p>\ - <p><strong>SMP instance name:</strong> ${SMP_INSTANCE_NAME}</p> - -# Email texts for credential suspended -mail.credential_suspended.title=Credential type: ${CREDENTIAL_TYPE} is temporarily suspended -mail.credential_suspended.content=<p><strong>Credential type:</strong> ${CREDENTIAL_TYPE}</p> \ - <p><strong>Credential identifier:</strong> ${CREDENTIAL_ID}</p> \ - <p><strong>Failed login attempt count:</strong> ${FAILED_LOGIN_ATTEMPT}</p> \ - <p><strong>Last failed login time:</strong> ${LAST_LOGIN_FAILURE_DATETIME}</p> \ - <p><strong>Suspended util</strong> ${SUSPENDED_UNTIL_DATETIME}</p> \ - <p><strong>Reporting time:</strong> ${REPORTING_DATETIME}</p> \ - <p><strong>Alert level:</strong> ${ALERT_LEVEL}</p> \ - <p><strong>SMP instance name:</strong> ${SMP_INSTANCE_NAME}</p> - -# Email texts for credential verification failed -mail.credential_verification_failed.title=Credential type: ${CREDENTIAL_TYPE} verification failed! -mail.credential_verification_failed.content=<p><strong>Credential type:</strong> ${CREDENTIAL_TYPE}</p> \ - <p><strong>Credential identifier:</strong> ${CREDENTIAL_ID}</p> \ - <p><strong>Failed login attempt count:</strong> ${FAILED_LOGIN_ATTEMPT}</p> \ - <p><strong>Last failed login time:</strong> ${LAST_LOGIN_FAILURE_DATETIME}</p> \ - <p><strong>Reporting time:</strong> ${REPORTING_DATETIME}</p> \ - <p><strong>Alert level:</strong> ${ALERT_LEVEL}</p> \ - <p><strong>SMP instance name:</strong> ${SMP_INSTANCE_NAME}</p> - diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/SMPLanguageResourceServiceTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/SMPLanguageResourceServiceTest.java new file mode 100644 index 000000000..76b0c7c5e --- /dev/null +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/SMPLanguageResourceServiceTest.java @@ -0,0 +1,137 @@ +package eu.europa.ec.edelivery.smp.services; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; + +import static eu.europa.ec.edelivery.smp.services.SMPLanguageResourceService.*; +import static org.junit.jupiter.api.Assertions.*; + +/** + * @since 5.1 + * @author Joze RIHTARSIC + */ +class SMPLanguageResourceServiceTest { + File localeFolder = new File("target/locales"); + ConfigurationService configurationService = Mockito.mock(ConfigurationService.class); + ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); + SMPLanguageResourceService testInstance = new SMPLanguageResourceService(configurationService, resourcePatternResolver); + + @BeforeEach + void setUp() throws IOException { + // clean the folder + if (localeFolder.exists() ) { + // first delete all files and then empty folders + Files.walk(localeFolder.toPath()) + .map( Path::toFile ) + .sorted( Comparator.comparing( File::isDirectory ) ) + .forEach( File::delete ); + } + Mockito.when(configurationService.getLocaleFolder()).thenReturn(localeFolder); + } + + @Test + void testMergeAddMissingProperty() { + // given + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode targetNode = objectMapper.createObjectNode(); + targetNode.put("key1", "value1"); + targetNode.put("key2", "value2"); + + ObjectNode referenceNode = objectMapper.createObjectNode(); + referenceNode.put("key1", "value-001"); + referenceNode.put("key2", "value-002"); + referenceNode.put("key3", "value-003"); + + // when + boolean result = mergeTranslationJson(targetNode, referenceNode); + + // then + assertTrue(result); + assertEquals("value1", targetNode.get("key1").asText()); + assertEquals("value2", targetNode.get("key2").asText()); + assertEquals("value-003", targetNode.get("key3").asText()); + } + + @Test + void testMergeAddMissingProperty2() { + // given + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode targetNode = objectMapper.createObjectNode(); + targetNode.put("key1", "value1"); + targetNode.put("key2", ""); + + ObjectNode referenceNode = objectMapper.createObjectNode(); + referenceNode.put("key1", "value-001"); + referenceNode.put("key2", "value-002"); + + // when + boolean result = mergeTranslationJson(targetNode, referenceNode); + + // then + assertFalse(result); + assertEquals("value1", targetNode.get("key1").asText()); + assertEquals("", targetNode.get("key2").asText()); + assertFalse(targetNode.has("key3")); + } + + @Test + void updateLocalesOnDiskCleanFolder() { + // given + assertFalse(localeFolder.exists()); + // when + testInstance.updateLocalesOnDisk(); + // then + assertTrue(localeFolder.exists()); + File[] files = localeFolder.listFiles(); + assertNotNull(files); + assertEquals(2, files.length); + assertEquals(LANGUAGE_FILENAME_UI_PREFIX + "en.json", files[0].getName()); + assertEquals(LANGUAGE_FILENAME_MAIL_PREFIX + "en.json", files[1].getName()); + } + + @Test + void updateLocalesOnDiskOverwrite() throws IOException { + // given + ObjectMapper objectMapper = new ObjectMapper(); + localeFolder.mkdirs(); + + String testText = "This must NOT be overwritten"; + String testKey = "column.selection.link.all"; + Path pathToFile = Paths.get(localeFolder.getAbsolutePath(), LANGUAGE_FILENAME_UI_PREFIX + "en.json"); + assertFalse(pathToFile.toFile().exists()); + // add one property + ObjectNode testNode = objectMapper.createObjectNode(); + testNode.put(testKey, testText); + objectMapper.writeValue(pathToFile.toFile(), testNode); + assertTrue(pathToFile.toFile().exists()); + + // when + testInstance.updateLocalesOnDisk(); + // then + assertTrue(localeFolder.exists()); + File[] files = localeFolder.listFiles(); + assertNotNull(files); + assertEquals(2, files.length); + assertEquals(LANGUAGE_FILENAME_UI_PREFIX + "en.json", files[0].getName()); + assertEquals(LANGUAGE_FILENAME_MAIL_PREFIX + "en.json", files[1].getName()); + + JsonNode result = objectMapper.readTree(pathToFile.toFile()); + assertEquals(testText, result.get(testKey).asText()); + // 3 properties are added by the updateLocalesOnDisk method + // from the classpath resource META-INF/resources/ui/assets/i18n/en.json + assertEquals(4, result.size()); + } +} diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/mail/MailTemplateTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/mail/MailTemplateTest.java index 1bea4fd48..44c85ef04 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/mail/MailTemplateTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/mail/MailTemplateTest.java @@ -19,9 +19,20 @@ package eu.europa.ec.edelivery.smp.services.mail; import eu.europa.ec.edelivery.smp.data.ui.enums.AlertTypeEnum; +import eu.europa.ec.edelivery.smp.services.ConfigurationService; +import eu.europa.ec.edelivery.smp.services.SMPLanguageResourceService; import eu.europa.ec.edelivery.smp.services.mail.prop.CredentialsExpirationProperties; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; import java.util.HashMap; import java.util.Map; @@ -30,8 +41,25 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class MailTemplateTest { + File localeFolder = new File("target/locales"); + ConfigurationService configurationService = Mockito.mock(ConfigurationService.class); + ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); + SMPLanguageResourceService smpLanguageResourceService = new SMPLanguageResourceService(configurationService, resourcePatternResolver); - MailTemplateService testInstance = new MailTemplateService(); + MailTemplateService testInstance = new MailTemplateService(smpLanguageResourceService); + + @BeforeEach + void setUp() throws IOException { + // clean the folder + if (localeFolder.exists() ) { + // first delete all files and then empty folders + Files.walk(localeFolder.toPath()) + .map( Path::toFile ) + .sorted( Comparator.comparing( File::isDirectory ) ) + .forEach( File::delete ); + } + Mockito.when(configurationService.getLocaleFolder()).thenReturn(localeFolder); + } @Test void getMailContent() { @@ -51,6 +79,5 @@ class MailTemplateTest { assertTrue(result.contains("alert level")); assertTrue(result.contains("credential id")); assertTrue(result.contains("credential name")); - } } diff --git a/smp-server-library/src/test/resources/META-INF/resources/ui/assets/i18n/en.json b/smp-server-library/src/test/resources/META-INF/resources/ui/assets/i18n/en.json new file mode 100644 index 000000000..9b0e73154 --- /dev/null +++ b/smp-server-library/src/test/resources/META-INF/resources/ui/assets/i18n/en.json @@ -0,0 +1,6 @@ +{ + "column.selection.link.all": "All", + "column.selection.link.none": "None", + "column.selection.link.show": "Show columns", + "column.selection.link.hide": "Hide columns" +} diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/LocaleController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/LocaleController.java index 4b30c9810..9c184efb2 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/LocaleController.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/LocaleController.java @@ -1,17 +1,14 @@ package eu.europa.ec.edelivery.smp.ui.external; -import eu.europa.ec.edelivery.smp.i18n.SMPLocale; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; -import eu.europa.ec.edelivery.smp.services.SMPLocaleService; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.FileSystemResource; +import eu.europa.ec.edelivery.smp.services.SMPLanguageResourceService; import org.springframework.core.io.Resource; import org.springframework.util.MimeTypeUtils; import org.springframework.web.bind.annotation.*; -import java.nio.file.Path; - +import static eu.europa.ec.edelivery.smp.services.SMPLanguageResourceService.LANGUAGE_FILENAME_UI_PREFIX; +import static eu.europa.ec.edelivery.smp.services.SMPLanguageResourceService.LANGUAGE_RESOURCE_UI_DEFAULT; import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.CONTEXT_PATH_PUBLIC_LOCALE; /** @@ -24,29 +21,16 @@ import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.CONTEXT_PATH_PUBLI @RequestMapping(value = CONTEXT_PATH_PUBLIC_LOCALE) public class LocaleController { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(LocaleController.class); - private static final String DEFAULT_LOCALE_RESOURCE = "/META-INF/resources/ui/assets/i18n/en.json"; - private final SMPLocaleService smpLocaleService; - public LocaleController(SMPLocaleService smpLocaleService) { + private final SMPLanguageResourceService smpLocaleService; + + public LocaleController(SMPLanguageResourceService smpLocaleService) { this.smpLocaleService = smpLocaleService; } @GetMapping(value = "/{code}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE) - @ResponseBody - public Resource getLocale(@PathVariable("code") String code) { - Path langResourcePath = smpLocaleService.getLocaleFile(SMPLocale.fromCodeDefaultingToEnglish(code)); - if (langResourcePath != null && langResourcePath.toFile().exists()) { - LOG.debug("Returning locale file [{}]", langResourcePath.toAbsolutePath()); - return new FileSystemResource(langResourcePath); - } else { - LOG.warn("Locale file [{}] does not exist. Return default translation!", langResourcePath.toAbsolutePath()); - ClassPathResource defResource = new ClassPathResource(DEFAULT_LOCALE_RESOURCE); - if (defResource.exists()) { - return defResource; - } else { - LOG.error("Default locale file [{}] does not exist in classpath!", DEFAULT_LOCALE_RESOURCE); - return null; - } - } + public Resource getLanguage(@PathVariable("code") String code) { + LOG.debug("Requesting locale file for code: [{}]", code); + return smpLocaleService.getTranslationResourceFile(LANGUAGE_FILENAME_UI_PREFIX, code, LANGUAGE_RESOURCE_UI_DEFAULT); } } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/UserAdminController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/UserAdminController.java index 4320a8f30..87e0667de 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/UserAdminController.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/UserAdminController.java @@ -172,6 +172,7 @@ public class UserAdminController { } DBUser user = uiUserService.updateUserPassword(authorizedUserId, changeUserId, newPassword.getCurrentPassword(), newPassword.getNewPassword(),!currentUser.isCasAuthenticated()); + return authorizationService.sanitize(uiUserService.convertToRo(user)); } -- GitLab