From 9ef696591e61884f6de769c2803b4f73c34b6f10 Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Tue, 20 Feb 2024 10:04:42 +0100 Subject: [PATCH 01/16] Bug Fixes AZUPF-55,56,30,52,62 --- .../rest/DataTransferPersonalBoxPageRest.kt | 1 - .../org/projectforge/mail/MailAccount.java | 10 +- .../main/resources/I18nResources.properties | 42 +- .../resources/I18nResources_de.properties | 1560 +++++++++-------- .../projectforge/rest/AddressViewPageRest.kt | 4 +- .../kotlin/org/projectforge/rest/dto/User.kt | 4 + .../rest/fibu/CustomerPagesRest.kt | 1 - .../projectforge/rest/poll/PollCronJobs.kt | 13 +- .../rest/poll/PollInfoPageRest.kt | 12 +- .../projectforge/rest/poll/PollMailService.kt | 22 +- .../projectforge/rest/poll/PollPageRest.kt | 81 +- .../rest/poll/PollResponsePageRest.kt | 2 +- .../rest/poll/types/PremadeQuestions.kt | 2 +- .../projectforge/rest/poll/types/Question.kt | 1 + 14 files changed, 983 insertions(+), 772 deletions(-) diff --git a/plugins/org.projectforge.plugins.datatransfer/src/main/kotlin/org/projectforge/plugins/datatransfer/rest/DataTransferPersonalBoxPageRest.kt b/plugins/org.projectforge.plugins.datatransfer/src/main/kotlin/org/projectforge/plugins/datatransfer/rest/DataTransferPersonalBoxPageRest.kt index 1773efe6d4..11d5168650 100644 --- a/plugins/org.projectforge.plugins.datatransfer/src/main/kotlin/org/projectforge/plugins/datatransfer/rest/DataTransferPersonalBoxPageRest.kt +++ b/plugins/org.projectforge.plugins.datatransfer/src/main/kotlin/org/projectforge/plugins/datatransfer/rest/DataTransferPersonalBoxPageRest.kt @@ -25,7 +25,6 @@ package org.projectforge.plugins.datatransfer.rest import org.projectforge.business.user.UserGroupCache import org.projectforge.business.user.service.UserPrefService -import org.projectforge.framework.i18n.translate import org.projectforge.framework.utils.NumberHelper import org.projectforge.model.rest.RestPaths import org.projectforge.plugins.datatransfer.DataTransferAreaDao diff --git a/projectforge-business/src/main/java/org/projectforge/mail/MailAccount.java b/projectforge-business/src/main/java/org/projectforge/mail/MailAccount.java index d351546191..2a47cac83d 100644 --- a/projectforge-business/src/main/java/org/projectforge/mail/MailAccount.java +++ b/projectforge-business/src/main/java/org/projectforge/mail/MailAccount.java @@ -35,7 +35,7 @@ /** * Connects to a mail server and receives mails. - * + * * @author Kai Reinhard (k.reinhard@micromata.de) */ public class MailAccount @@ -75,7 +75,7 @@ public MailAccount(Store store, Folder folder) /** * Gets a list of all Emails matching the given filter. - * + * * @return ArrayList of all found Email. */ public Mail[] getMails(final MailFilter filter) @@ -134,7 +134,7 @@ public Mail[] getMails(final MailFilter filter) /** * Opens the connection to the mailserver. Don't forget to call disconnect if this method returns true! - * + * * @param mbox The folder name to open. If null then the default folder will be opened. * @param readwrite If false then the mbox is connected in readonly mode. * @return true on success, otherwise false. @@ -207,7 +207,7 @@ public Mail[] getMails(final MailFilter filter) /** * Disconnects the folder and store if given and is opened yet. - * + * * @return */ // @Deprecated @@ -360,4 +360,4 @@ private void getContent(final Part msg, final StringBuffer buf) throws Messaging } } } -} +} \ No newline at end of file diff --git a/projectforge-business/src/main/resources/I18nResources.properties b/projectforge-business/src/main/resources/I18nResources.properties index c84d171c90..d52c25cf05 100644 --- a/projectforge-business/src/main/resources/I18nResources.properties +++ b/projectforge-business/src/main/resources/I18nResources.properties @@ -187,7 +187,14 @@ moreEntriesAvailable=More entries available. name=Name new=New nickname=Nickname -no=No + + + + + + + + notEnded=not ended nothingFound=Nothing found. notLoggedIn=Not logged in. @@ -281,7 +288,6 @@ username=User name value=Value values=Values weekOfYear=Week of year -yes=Yes access=Accessright access.accessTable=Access table @@ -1976,10 +1982,19 @@ plugins.teamcal.title.edit=Edit team calendar plugins.teamcal.title.heading=Calendar plugins.teamcal.title.list=List of calendars + + + + # poll plugin +TextQuestion=Text Question +SingleResponseQuestion=Single Response Question +MultiResponseQuestion=Multiple Choice Question + + poll=Poll poll.access=Access -poll.answer=Answer +poll.answer=Option poll.assignment=Assignment poll.attendee=Attendee poll.attendees=Attendees @@ -1990,6 +2005,7 @@ poll.confirmation.creation=Do you really want to create the poll? You won't be a Also make sure to add attendees for your poll. poll.confirmation.deleteAnswer=Do you really want to delete this answer? poll.confirmation.deleteQuestion=Do you really want to delete this question? +poll.confirmation.deleteButton=Yes poll.confirmation.finish=Do you really want to finish this Poll? poll.date=Date poll.deadline=Deadline @@ -2036,8 +2052,26 @@ poll.other=Other poll.owner=Owner poll.popup.closed=The poll was already closed. poll.question=Question -poll.question.textQuestion=Text Question poll.questionType=Question Type +poll.questionType.TextQuestion=Text Question +poll.questionType.SingleResponseQuestion=Single response Question +poll.questionType.MultiResponseQuestion=Multiple Choice Questions + +poll.question.texttitle= Text Questions +poll.question.singletitel= Singel response Question +poll.question.multititle= Multiple Choice Questions + +poll.question.text=Question +poll.question.single=Question +poll.question.multi=Question + + + + + + + + poll.respond=Send responses poll.response.mail.update.content=

Dear {0},

\

I wanted to inform you that Person {2} has updated their answer to Poll {1}.

\ diff --git a/projectforge-business/src/main/resources/I18nResources_de.properties b/projectforge-business/src/main/resources/I18nResources_de.properties index 3d7a6b76e9..4ce0303f60 100644 --- a/projectforge-business/src/main/resources/I18nResources_de.properties +++ b/projectforge-business/src/main/resources/I18nResources_de.properties @@ -93,23 +93,23 @@ # Wicket: -datatable.no-records-found=Keine Einträge gefunden. +datatable.no-records-found=Keine Einträge gefunden. NavigatorLabel=Zeile ${from} bis ${to} von ${of}. StringValidator.exact=Das Feld ''${label}'' ist nicht exakt ${exact} Zeichen lang, sondern ${length} Zeichen lang. StringValidator.maximum=Das Feld ''${label}'' mit ${length} Zeichen darf maximal ${maximum} Zeichen lang sein. StringValidator.minimum=Das Feld ''${label}'' mit ${length} Zeichen muss mindestens ${minimum} Zeichen lang sein. StringValidator.range=Das Feld ''${label}'' mit ${length} Zeichen muss zwischen ${minimum} und ${maximum} Zeichen lang sein. -validation.error.fieldRequired=Feld ''{0}'' muss ausgefüllt werden. -validation.error.format.integer=Ungültiger Ganzzahlwert. -validation.error.generic=Ungültige Eingabe. -validation.error.nothingToExport=Keine Daten für den Export vorhanden. -validation.error.range.integerOutOfRange=Wert außerhalb des Bereichs {0} - {1}. -validation.error.range.integerToHigh=Wert darf nicht größer als {0} sein. +validation.error.fieldRequired=Feld ''{0}'' muss ausgefüllt werden. +validation.error.format.integer=Ungültiger Ganzzahlwert. +validation.error.generic=Ungültige Eingabe. +validation.error.nothingToExport=Keine Daten für den Export vorhanden. +validation.error.range.integerOutOfRange=Wert außerhalb des Bereichs {0} - {1}. +validation.error.range.integerToHigh=Wert darf nicht größer als {0} sein. validation.error.range.integerToLow=Wert darf nicht kleiner als {0} sein. validation.required.valueNotPresent=Wert ''{0}'' nicht gegeben. # React UI -select.placeholder=Auswählen... +select.placeholder=Auswählen... table.showing=Anzeige # own validations: @@ -117,7 +117,7 @@ bicvalidator.wronglength=Das Feld ''${label}'' muss 8 oder 11 Zeichen lang sein. ibanvalidator.wronglength.de=Das Feld ''${label}'' beginnt mit ''DE''. Eine deutsche IBAN muss jedoch aus 22 Zeichen bestehen. # Currency format -currencyConverter.percentage.help=Es können sowohl Beträge als auch Prozentzahlen (z. B. 10%) eingegeben werden. +currencyConverter.percentage.help=Es können sowohl Beträge als auch Prozentzahlen (z. B. 10%) eingegeben werden. currencyFormat={0,number,,##0.00} # Not internationalized properties: @@ -131,73 +131,75 @@ currencyFormat={0,number,,##0.00} ### not translated: message.notYetImplemented=Not yet implemented. # Buttons: -add=Hinzufügen +add=Hinzufügen assign=Zuweisen -back=Zurück +back=Zurück cancel=Abbrechen -change=Ändern + +create=Anlegen +change=Ändern check=Check clone=Klonen -close=Schließen +close=Schließen copy=Kopieren -create=Anlegen -delete=Löschen -deselectAll=Alle abwählen -download=Download -download.expired=Downloaddatei nicht mehr verfügbar. Bitte Aktion wiederholen. -drop=Drop -execute=Ausführen -exportAsPdf=Pdf-Export -exportAsXls=Excel-Export +delete=Löschen +deselectAll=Alle abwählen +download=Herunterladen +download.expired=Downloaddatei nicht mehr verfügbar. Bitte Aktion wiederholen. +drop=Verwerfern +execute=Ausführen +exportAsPdf=Pdf-Exportieren +exportAsXls=Excel-Exportieren favorite=Favorit favorite.addNew=Neuer Favorit favorite.filter.addNew=Neuer Filter favorite.untitled=unbenannt favorites=Favoriten -favorites.saveModification=Änderungen speichern +favorites.saveModification=Änderungen speichern finish=Fertig -forceDelete=Unwiderruflich löschen +Button_Finish=Ja +forceDelete=Unwiderruflich löschen import=Importieren login=Anmelden mark=Markieren -markAsDeleted=Löschen -massUpdate=Massenänderung +markAsDeleted=Löschen +massUpdate=Massenänderung more=Mehr next=Weiter ok=OK -paste=Einfügen +paste=Einfügen preview=Vorschau recalculate=Neu berechnen redraw=Neu zeichnen refresh=Neu laden reload=Neu laden rename=Umbenennen -reset=Rücksetzen +reset=Rücksetzen save=Speichern search=Suchen -select=Auswählen -selectAll=Alle auswählen -selectDate=Auswählen -selectGroup=Auswählen -selectTask=Auswählen +select=Auswählen +selectAll=Alle auswählen +selectDate=Auswählen +selectGroup=Auswählen +selectTask=Auswählen send=Senden show=Anzeigen stop=Beenden unassign=Entfernen undelete=Wiederherstellen unkown=unbekannt -update=Ändern -updateAll=Alle ändern -updateAndNext=Ändern und nächster +update=Ändern +updateAll=Alle ändern +updateAndNext=Ändern und nächster upload=Hochladen uptodate=aktuell wizard=Assistent # Common akquise=Akquise -changes=Änderungen -charactersLeft=Zeichen übrig. -color.error.unknownFormat=Farbcode kann nicht verarbeitet werden. Unterstützte Hex-Farbformate: #abc or #abcdef +changes=Änderungen +charactersLeft=Zeichen übrig. +color.error.unknownFormat=Farbcode kann nicht verarbeitet werden. Unterstützte Hex-Farbformate: #abc or #abcdef comment=Bemerkung completed=erledigt contactPerson=Ansprechpartner:in @@ -214,9 +216,9 @@ day=Tag(e) days=Tage deadline=Frist default=Standard -deleted=gelöscht +deleted=gelöscht description=Beschreibung -dueDate=Fälligkeit +dueDate=Fälligkeit edit=Bearbeiten email=E-Mail ended=beendet @@ -225,22 +227,22 @@ errors=Fehler export=Export fieldNotHistorizable=Dieses Feld wird nicht historisiert! file=Datei -file.upload.audit.action.delete=gelöscht +file.upload.audit.action.delete=gelöscht file.upload.audit.action.download=heruntergeladen file.upload.audit.action.downloadAll=alles heruntergeladen file.upload.audit.action.downloadMulti=mehrere heruntergeladen -file.upload.audit.action.modification=geändert (Dateiname oder Beschreibung) +file.upload.audit.action.modification=geändert (Dateiname oder Beschreibung) file.upload.audit.action.upload=hochgeladen -file.upload.choose=Datei wählen -file.upload.deleteSelected=Ausgewählte Dateien löschen -file.upload.deleteSelected.confirm=Sollen jetzt wirklich alle ausgewählten Dateien unwiderruflich gelöscht werden? -file.upload.downloadSelected=Ausgewählte Dateien herunterladen -file.upload.dropArea=Datei auswählen oder hier hinziehen. +file.upload.choose=Datei wählen +file.upload.deleteSelected=Ausgewählte Dateien löschen +file.upload.deleteSelected.confirm=Sollen jetzt wirklich alle ausgewählten Dateien unwiderruflich gelöscht werden? +file.upload.downloadSelected=Ausgewählte Dateien herunterladen +file.upload.dropArea=Datei auswählen oder hier hinziehen. file.upload.error.fileAlreadyExists=Die Datei ''{0}'' existiert bereits. -file.upload.error.maxSizeExceeded=Datei ''{0}'' kann nicht hochgeladen werden. Die konfigurierte Maximalgröße wurde überschritten: {1}>{2}. -file.upload.error.maxSizeOfExceeded=Die Datei konnte nicht hochgeladen werden. Die Maximalgröße von {0} wurde überschritten. -file.upload.error.noFileSelected=Keine Datei gewählt -file.upload.error.toManyFiles=Es wurden zu viele Dateien für das gleichzeitige Hochladen ausgewählt. +file.upload.error.maxSizeExceeded=Datei ''{0}'' kann nicht hochgeladen werden. Die konfigurierte Maximalgröße wurde überschritten: {1}>{2}. +file.upload.error.maxSizeOfExceeded=Die Datei konnte nicht hochgeladen werden. Die Maximalgröße von {0} wurde überschritten. +file.upload.error.noFileSelected=Keine Datei gewählt +file.upload.error.toManyFiles=Es wurden zu viele Dateien für das gleichzeitige Hochladen ausgewählt. filing=Ablageort filter=Filter filter.all=alle @@ -252,7 +254,7 @@ firstName=Vorname gender=Geschlecht gender.diverse=divers gender.female=weiblich -gender.male=männlich +gender.male=männlich gender.notApplicable=nicht zutreffend gender.notKnown=unbekannt gender.unknown=unbekannt @@ -262,9 +264,9 @@ hours=Stunden id=Id imageFile=Bild inherit=vererbt -key=Schlüssel +key=Schlüssel language=Sprache -lastUpdate=letzte Änderung +lastUpdate=letzte Änderung legend=Legende list=Liste listView=Listenansicht @@ -272,36 +274,35 @@ loading=Laden... loggedInUserInfo=Angemeldet als: longFormat=Langes Format misc=Verschiedenes -modifications=Änderungen -modificationTime=Änderungszeitraum -modified=geändert -modifiedBy=geändert durch -modifiedHistoryValue=Wert in der Änderungshistorie -moreEntriesAvailable=Mehr Einträge vorhanden. +modifications=Änderungen +modificationTime=Änderungszeitraum +modified=geändert +modifiedBy=geändert durch +modifiedHistoryValue=Wert in der Änderungshistorie +moreEntriesAvailable=Mehr Einträge vorhanden. name=Name new=Neu nickname=Rufname -no=Nein notEnded=nicht beendet nothingFound=Nichts gefunden. notLoggedIn=Nicht angemeldet notVisible=nicht sichtbar oclock=Uhr -onlyDeleted=nur gelöschte -onlyDeleted.tooltip=Es werden nur gelöschte Datensätze angezeigt (i. d. R. unabhängig von anderen Filterangaben). -operation.deleted=gelöscht +onlyDeleted=nur gelöschte +onlyDeleted.tooltip=Es werden nur gelöschte Datensätze angezeigt (i. d. R. unabhängig von anderen Filterangaben). +operation.deleted=gelöscht operation.inserted=angelegt -operation.markAsDeleted=als gelöscht markiert +operation.markAsDeleted=als gelöscht markiert operation.undefined=undefiniert operation.undeleted=wiederhergestellt -operation.updated=geändert +operation.updated=geändert organization=Firma password=Passwort passwordRepeat=Passwort wiederholen percent=Prozent -pleaseChoose=Bitte wählen +pleaseChoose=Bitte wählen printView=Druckansicht -priority=Priorität +priority=Priorität priority.high=hoch priority.highest=sehr hoch priority.least=gering @@ -360,7 +361,7 @@ timeNotation=Uhrzeitformat timeNotation.12=12 Stunden timeNotation.24=24 Stunden timeOfCreation=Anlagezeitpunkt -timeOfLastUpdate=Zeitpunkt der letzten Änderung +timeOfLastUpdate=Zeitpunkt der letzten Änderung timePeriod=Zeitraum timestamp=Zeitstempel timezone=Zeitzone @@ -375,27 +376,26 @@ username=Benutzer:innenname value=Wert values=Werte weekOfYear=Kalenderwoche -yes=Ja access=Zugriffsrecht access.accessTable=Zugriffstabelle -access.exception.demoUserHasNoAccess=Der oder die Demobenutzer:in ist für diese Aktion gesperrt. +access.exception.demoUserHasNoAccess=Der oder die Demobenutzer:in ist für diese Aktion gesperrt. access.exception.employeeHasNoVacationDays=Dem oder der Mitarbeiter:in sind keine Urlaubstage zugewiesen access.exception.noAccess=Kein Zugriff. -access.exception.noEmployeeToUser=Angemeldete:r Benutzer:in hat keinen zugehörige:n Mitarbeiter:in +access.exception.noEmployeeToUser=Angemeldete:r Benutzer:in hat keinen zugehörige:n Mitarbeiter:in access.exception.noReadAccess=Kein lesender Zugriff. access.exception.noUserGiven=Keine:n angemeldete:n Benutzer:in gefunden. access.exception.noWriteAccess=Kein schreibender Zugriff. -access.exception.restrictedUserHasNoAccess=Der oder der eingeschänkte (restricted) Benutzer:in ist für diese Aktion gesperrt. +access.exception.restrictedUserHasNoAccess=Der oder der eingeschänkte (restricted) Benutzer:in ist für diese Aktion gesperrt. access.exception.standard=Zugriffsverletzung: {0} - {1}. -access.exception.standardWithTask=Zugriffsverletzung für Strukturelement\: ''{0}''\: {1} - {2}. +access.exception.standardWithTask=Zugriffsverletzung für Strukturelement\: ''{0}''\: {1} - {2}. access.exception.userHasNotRight=Der oder die Benutzer:in hat nicht das geforderte Recht {0} - {1}. access.exception.violation=Zugriffsverletzung: {0}. access.filter.includeAncestorTasks=Strukturoberelemente access.filter.includeDescendentTasks=Strukturunterelemente access.groups=Gruppen access.read=Lesezugriff -access.recursive.help=Rekursiv gilt auch für alle Strukturunterelemente. +access.recursive.help=Rekursiv gilt auch für alle Strukturunterelemente. access.right.admin.core=Adminrights ### not translated: access.right.admin.multitenancy=Multi-tenancy administration access.right.category.fibu=Finanzbuchhaltung @@ -405,15 +405,15 @@ access.right.category.plugins=Plugins access.right.category.pm=Projektmanagement access.right.fibu.accounts=Konten access.right.fibu.ausgangsrechnungen=Debitorenrechnungen -access.right.fibu.costUnit=Kostenträger +access.right.fibu.costUnit=Kostenträger access.right.fibu.datevImport=Datev-Import access.right.fibu.eingangsrechnungen=Kreditorenrechnungen access.right.fibu.employee=Mitarbeiter:in -access.right.fibu.employeeSalaries=Gehälter +access.right.fibu.employeeSalaries=Gehälter access.right.hr.employee=Mitarbeiter:in -access.right.hr.employeeSalaries=Gehälter +access.right.hr.employeeSalaries=Gehälter access.right.hr.vacation=Urlaub -access.right.orga.contracts=Verträge +access.right.orga.contracts=Verträge access.right.orga.incomingmail=Eingangspost access.right.orga.outgoingmail=Ausgangspost access.right.orga.visitorbook=Besuchsbuch @@ -438,17 +438,17 @@ access.title.add=Neues Zugriffsrecht access.title.edit=Zugriff bearbeiten access.title.heading=Zugriffsrechte access.title.list=Liste der Zugriffsrechte -access.tooltip.deleteAccess=Löschen-Recht (Einträge löschen) -access.tooltip.filter.includeAncestorTasks=Auch für alle Strukturoberelemente die definierte Zugriffsrechte anzeigen (ein Strukturelement muss ausgewählt sein). -access.tooltip.filter.includeDescendentTasks=Auch für alle Strukturunterelemente die definierte Zugriffsrechte anzeigen (ein Strukturelement muss ausgewählt sein). -access.tooltip.filter.inherit=Auch von allen Strukturoberelementen vererbte Zugriffrechte anzeigen (ein Strukturelement muss ausgewählt sein). -access.tooltip.filter.user=Zeige alle definierten Zugriffsrechten zu Gruppen, denen der ausgewählte Benutzer angehört. -access.tooltip.insertAccess=Anlegen-Recht (neue Einträge hinzufügen) +access.tooltip.deleteAccess=Löschen-Recht (Einträge löschen) +access.tooltip.filter.includeAncestorTasks=Auch für alle Strukturoberelemente die definierte Zugriffsrechte anzeigen (ein Strukturelement muss ausgewählt sein). +access.tooltip.filter.includeDescendentTasks=Auch für alle Strukturunterelemente die definierte Zugriffsrechte anzeigen (ein Strukturelement muss ausgewählt sein). +access.tooltip.filter.inherit=Auch von allen Strukturoberelementen vererbte Zugriffrechte anzeigen (ein Strukturelement muss ausgewählt sein). +access.tooltip.filter.user=Zeige alle definierten Zugriffsrechten zu Gruppen, denen der ausgewählte Benutzer angehört. +access.tooltip.insertAccess=Anlegen-Recht (neue Einträge hinzufügen) access.tooltip.selectAccess=Lesen-Recht -access.tooltip.updateAccess=Änderungs-Recht (bestehende Einträge bearbeiten) +access.tooltip.updateAccess=Änderungs-Recht (bestehende Einträge bearbeiten) access.type=Zugriffstyp access.type.accessManagement=Zugriffsverwaltung -access.type.delete=Löschen +access.type.delete=Löschen access.type.group=Gruppen access.type.insert=Anlegen access.type.ownTimesheets=Eigene Zeitberichte @@ -456,13 +456,13 @@ access.type.select=Lesen access.type.tasks=Strukturelemente access.type.timesheets=Zeitberichte access.type.undelete=Wiederherstellen -access.type.update=Ändern +access.type.update=Ändern access.users=Benutzer:in -access.violation.userNotMemberOf=Operation ist nur für Benutzer:innen der Gruppe(n) ''{0}'' erlaubt (ggf. auch andere Gruppen möglich). +access.violation.userNotMemberOf=Operation ist nur für Benutzer:innen der Gruppe(n) ''{0}'' erlaubt (ggf. auch andere Gruppen möglich). access.write=Schreibzugriff -address.accessException.userHasNoRightForAddressbook=Benutzer:in hat keine Berechtigung für die ausgewählte Adresse und dessen Addressbuch -address.accessException.userIsNotOwnerOfPersonalAddress=Benutzer:in ist nicht Besitzer:in der persönlichen Adresse. -address.addressbooks=Adressbücher +address.accessException.userHasNoRightForAddressbook=Benutzer:in hat keine Berechtigung für die ausgewählte Adresse und dessen Addressbuch +address.accessException.userIsNotOwnerOfPersonalAddress=Benutzer:in ist nicht Besitzer:in der persönlichen Adresse. +address.addressbooks=Adressbücher address.addresses=Adressen address.addressStatus=Adressstatus address.addressStatus.leaved=Unternehmen verlassen @@ -476,25 +476,25 @@ address.birthday=Geburtstag address.birthName=Geburtsname address.book.export=Excel-Export address.book.export.appleScript4Notes=Apple-Script -address.book.export.appleScript4Notes.tooltip=Zum Löschen von Notizen (mit Mehrfacheinträgen) aus den aus ProjectForge exportierten Adressen (s. Handbuch). Anschließend sollten die Adressen erneut importiert werden, um die bereinigten Notizen aus ProjectForge zu importieren. -address.book.export.tooltip=Exportiert die gefilterten Adressen (nur Favoriten) als Excel-Tabelle mit allen Feldern. Eine Komplettliste kann aus Datenschutzgründen nur über die Finanzbuchhaltung abgerufen werden. -address.book.hasNoVCards=Es sind keine persönlichen Adressen als Favoriten hinterlegt. Bitte erst Adressen zur persönlichen vCard-Liste hinzufügen. +address.book.export.appleScript4Notes.tooltip=Zum Löschen von Notizen (mit Mehrfacheinträgen) aus den aus ProjectForge exportierten Adressen (s. Handbuch). Anschließend sollten die Adressen erneut importiert werden, um die bereinigten Notizen aus ProjectForge zu importieren. +address.book.export.tooltip=Exportiert die gefilterten Adressen (nur Favoriten) als Excel-Tabelle mit allen Feldern. Eine Komplettliste kann aus Datenschutzgründen nur über die Finanzbuchhaltung abgerufen werden. +address.book.hasNoVCards=Es sind keine persönlichen Adressen als Favoriten hinterlegt. Bitte erst Adressen zur persönlichen vCard-Liste hinzufügen. address.book.vCardExport=Export vCards -address.book.vCardExport.tooltip.content=VCards markierter Adressen können für externe Adressprogramme (z. B. Apple Adressbuch) exportiert werden (utf-8 sollte im Adressprogramm eingestellt sein). -address.book.vCardExport.tooltip.title=Persönliches Adressbuch +address.book.vCardExport.tooltip.content=VCards markierter Adressen können für externe Adressprogramme (z. B. Apple Adressbuch) exportiert werden (utf-8 sollte im Adressprogramm eingestellt sein). +address.book.vCardExport.tooltip.title=Persönliches Adressbuch address.book.vCardImport=Import vCard address.book.vCardImport.compare=Adressvergleichsseite address.book.vCardImport.existingEntry=Eintrag ist schon vorhanden. -address.book.vCardImport.fileUploadPanel=Datei auswählen -address.book.vCardImport.noFile=Keine Datei ausgewählt! +address.book.vCardImport.fileUploadPanel=Datei auswählen +address.book.vCardImport.noFile=Keine Datei ausgewählt! address.book.vCardImport.title.add=vCard importieren -address.book.vCardImport.tooltip=Hier Dateipfad einfügen. +address.book.vCardImport.tooltip=Hier Dateipfad einfügen. address.book.vCardImport.wrongFileType=Falsches Dateiformat. Bitte nur ".vcf"-Format verwenden. address.book.vCardSingleExport=Export vCard -address.business=geschäftlich +address.business=geschäftlich address.cardDAV.infopage.description=Der CardDAV-Service von ProjectForge kann genutzt werden, indem ein CardDAV-Account in der Clientsoftware wie z. B. im Apple Adressbuch eingerichtet wird. -address.cardDAV.infopage.description2=Es werden die persönlichen Favoriten synchronisiert. Wenn Adressen per CardDAV-Client gelöscht werden, so werden die Adressen nicht gelöscht, sondern lediglich als Favorit entfernt. -address.cardDAV.infopage.description3=Das Anlegen und Editieren von Adressen kann nur direkt in ProjectForge getätigt werden, um Dupletten zu verhindern. +address.cardDAV.infopage.description2=Es werden die persönlichen Favoriten synchronisiert. Wenn Adressen per CardDAV-Client gelöscht werden, so werden die Adressen nicht gelöscht, sondern lediglich als Favorit entfernt. +address.cardDAV.infopage.description3=Das Anlegen und Editieren von Adressen kann nur direkt in ProjectForge getätigt werden, um Dupletten zu verhindern. address.cardDAV.infopage.title=Einrichten des CardDAV-Services address.city=Stadt address.columnHead.myFavorites=F @@ -510,12 +510,12 @@ address.country=Land address.directCall.call=Anrufen address.directCall.confirm=Soll jetzt eine Telefonverbindung von Telefon ''{0}'' aus initiiert werden? address.directCall.destinationPhone=Zielrufnummer -address.directCall.noPhoneDefined=Es ist kein Telefon ausgewählt (bitte auswählen bzw. unter Mein Zugang festlegen). -address.directCall.number.tooltip=Anstelle einer Rufnummer können auch Vor- und Nachnamen sowie Firmennamen eingegeben werden oder aus der Adressliste eine Nummer angeklickt werden. +address.directCall.noPhoneDefined=Es ist kein Telefon ausgewählt (bitte auswählen bzw. unter Mein Zugang festlegen). +address.directCall.number.tooltip=Anstelle einer Rufnummer können auch Vor- und Nachnamen sowie Firmennamen eingegeben werden oder aus der Adressliste eine Nummer angeklickt werden. address.division=Bereich address.emails=E-Mails -address.error.phone.invalidFormat=Es sind nur Zahlen sowie '+' am Anfang, '-', '/' und Leerzeichen erlaubt. Die führende Ländervorwahl ist zwingend. Beispiel\: +49 561 316793-0 -address.favorites.info=Favoriten können über CardDAV mit einem Adressbuch synchronisiert werden oder als Excel exportiert werden. +address.error.phone.invalidFormat=Es sind nur Zahlen sowie '+' am Anfang, '-', '/' und Leerzeichen erlaubt. Die führende Ländervorwahl ist zwingend. Beispiel\: +49 561 316793-0 +address.favorites.info=Favoriten können über CardDAV mit einem Adressbuch synchronisiert werden oder als Excel exportiert werden. address.filter.doublets=Dupletten address.filter.images=Adressen mit Bildern address.filter.myFavorites=meine Favoriten @@ -528,32 +528,32 @@ address.form.miss=Frau address.form.mister=Herr address.form.unknown=unbekannt address.formerly=geb. -address.heading.businessAddress=Geschäftsadresse -address.heading.businessContact=Geschäftlicher Kontakt -address.heading.personalData=Persönliche Daten +address.heading.businessAddress=Geschäftsadresse +address.heading.businessContact=Geschäftlicher Kontakt +address.heading.personalData=Persönliche Daten address.heading.postalAddress=Briefanschrift address.heading.postalAddress2=Briefanschrift 2 address.heading.privateAddress=Privatadresse address.heading.privateAddress2=Privatadresse 2 address.heading.privateContact=Privater Kontakt address.image=Bild -address.image.upload.error=Bild kann nicht hochgeladen werden. Bisher wird nur das Bildformat PNG unterstützt mit der Maximalgröße von {0}. +address.image.upload.error=Bild kann nicht hochgeladen werden. Bisher wird nur das Bildformat PNG unterstützt mit der Maximalgröße von {0}. address.mailing=Mailing address.myCurrentCallerId=Anruferkennung address.myCurrentCallerId.tooltip.content=Diese Anruferkennung wird dem/der Angerufenen angezeigt. address.myCurrentCallerId.tooltip.title=Direkwahl address.myCurrentPhoneId=Mein Telefon -address.myCurrentPhoneId.tooltip.content=Unter Mein Zugang können persönliche Zuordnungen zu firmen-internen Telefonen vorgenommen werden (interne Rufnummer). Wird auf eine Telefonnummer in der Adressliste geklickt, so kann eine Direktwahl für das hier angegebene Telefon initiiert werden. +address.myCurrentPhoneId.tooltip.content=Unter Mein Zugang können persönliche Zuordnungen zu firmen-internen Telefonen vorgenommen werden (interne Rufnummer). Wird auf eine Telefonnummer in der Adressliste geklickt, so kann eine Direktwahl für das hier angegebene Telefon initiiert werden. address.myCurrentPhoneId.tooltip.title=Direktwahl address.phone=Telefon -address.phoneCall.number.invalid=Ungültige Telefonnummer. +address.phoneCall.number.invalid=Ungültige Telefonnummer. address.phoneCall.number.label=Telefonnummer -address.phoneCall.number.labeldescription=Zu wählen -address.phoneCall.result.callingError=Fehler beim Wählen. -address.phoneCall.result.successful=Erfolgreich gewählt. +address.phoneCall.number.labeldescription=Zu wählen +address.phoneCall.result.callingError=Fehler beim Wählen. +address.phoneCall.result.successful=Erfolgreich gewählt. address.phoneCall.title=Direktwahl Telefonanlage address.phoneNumbers=Telefonnummern -address.phoneType.business=Telefon geschäftlich +address.phoneType.business=Telefon geschäftlich address.phoneType.fax=Fax address.phoneType.mobile=Telefon mobil address.phoneType.private=Telefon privat @@ -565,13 +565,13 @@ address.private=privat address.privateAddressText=Privatadresse address.privateEmail=Private E-Mail address.publicKey=Public key -address.question.changeName=Soll der Name der Adresse "{1}, {0}" wirklich geändert werden? +address.question.changeName=Soll der Name der Adresse "{1}, {0}" wirklich geändert werden? address.sendSms.doNotReply=Sent by ProjectForge! address.sendSms.message=Nachricht address.sendSms.phoneNumber=Mobilfunknummer -address.sendSms.phoneNumber.info=Hier kann eine Mobilfunknummer eingegeben oder über Namen eine aus dem Adressbuch gesucht werden. +address.sendSms.phoneNumber.info=Hier kann eine Mobilfunknummer eingegeben oder über Namen eine aus dem Adressbuch gesucht werden. address.sendSms.sendMessage.result.messageError=Fehler in Nachricht. -address.sendSms.sendMessage.result.messageToLarge=Nachricht ist größer als 160 Zeichen. +address.sendSms.sendMessage.result.messageToLarge=Nachricht ist größer als 160 Zeichen. address.sendSms.sendMessage.result.successful=Nachricht wurde erfolgreich an {0} versendet: {1} address.sendSms.sendMessage.result.unknownError=Unbekannter Fehler. address.sendSms.sendMessage.result.wrongOrMissingNumber=Rufnummer fehlerhaft bzw. fehlt. @@ -584,8 +584,8 @@ address.title.edit=Adresse bearbeiten address.title.heading=Adressmanagement address.title.list=Adressliste address.title.view=Adressansicht -address.tooltip.phonelist=Die Nummer zur persönlichen Telefonliste hinzufügen. -address.tooltip.vCardList=Die Adresse zur persönlichen vCard-Liste hinzufügen. +address.tooltip.phonelist=Die Nummer zur persönlichen Telefonliste hinzufügen. +address.tooltip.vCardList=Die Adresse zur persönlichen vCard-Liste hinzufügen. address.tooltip.writeSMS=SMS schreiben address.view.title=Adresse ansehen address.website=Webseite @@ -603,34 +603,34 @@ addressbook.owner=Eigner:in addressbook.readonlyAccess=Nur lesend addressbook.readonlyAccess.tooltip=Diese Benutzer:innen haben einen reinen Lesezugriff auf alle Adressen, die diesem Adressbuch zugeordnet sind. addressbook.title=Titel -addressbook.title.add=Adressbuch hinzufügen +addressbook.title.add=Adressbuch hinzufügen addressbook.title.edit=Adressbuch editieren addressbook.title.heading=Adressbuch -addressbook.title.list=Adressbücherliste +addressbook.title.list=Adressbücherliste administration.configuration=Konfiguration -administration.configuration.param.calendarDomain=Kalenderdomäne -administration.configuration.param.calendarDomain.description=Die Domäne wird vom Teamkalender-Modul benötigt, um weltweit eindeutige Ereignis-IDs zu erzeugen. Verwenden Sie Zeichenketten der Art 'projectforge.my-company.com' und vermeiden Sie nachträgliche Änderungen. -administration.configuration.param.countryPhonePrefix=Standardländervorwahl -administration.configuration.param.countryPhonePrefix.description=Wird für die Konvertierung von Telefonnummern genutzt (Standardwert ist +49). Für alle Telefonnummern, die mit diesem Ländercode beginnen wird dieser Prefix durch eine 0 ersetzt (z. B. bei der Funktion Direktwahl). Für alle andere Telefonnummern wird der Ländercode durch 00 und Ländercode ersetzt (+49 123 -> 0123; +31 123 -> 0031123). +administration.configuration.param.calendarDomain=Kalenderdomäne +administration.configuration.param.calendarDomain.description=Die Domäne wird vom Teamkalender-Modul benötigt, um weltweit eindeutige Ereignis-IDs zu erzeugen. Verwenden Sie Zeichenketten der Art 'projectforge.my-company.com' und vermeiden Sie nachträgliche Änderungen. +administration.configuration.param.countryPhonePrefix=Standardländervorwahl +administration.configuration.param.countryPhonePrefix.description=Wird für die Konvertierung von Telefonnummern genutzt (Standardwert ist +49). Für alle Telefonnummern, die mit diesem Ländercode beginnen wird dieser Prefix durch eine 0 ersetzt (z. B. bei der Funktion Direktwahl). Für alle andere Telefonnummern wird der Ländercode durch 00 und Ländercode ersetzt (+49 123 -> 0123; +31 123 -> 0031123). administration.configuration.param.dateFormats=Datumsformate -administration.configuration.param.dateFormats.description=Diese Datumsformate werden unterstützt. Für jede:n Benutzer:in kann ein Format aus dieser komma-separierten Liste ausgewählt werden. Bei der Anlage neue:r Benutzer:in wird der erste Eintrag vorgewählt. -administration.configuration.param.defaultTask4Books=Standardstrukturelement (Id) für Bücher +administration.configuration.param.dateFormats.description=Diese Datumsformate werden unterstützt. Für jede:n Benutzer:in kann ein Format aus dieser komma-separierten Liste ausgewählt werden. Bei der Anlage neue:r Benutzer:in wird der erste Eintrag vorgewählt. +administration.configuration.param.defaultTask4Books=Standardstrukturelement (Id) für Bücher administration.configuration.param.defaultTask4Books.description=Nicht mehr benutzt. -administration.configuration.param.excelDateFormats=Datumsformate für Excel -administration.configuration.param.excelDateFormats.description=Diese Datumsformate werden in Excel-Exporten unterstützt. Für jede:n Benutzer:in kann ein Format aus dieser komma-separierten Liste ausgewählt werden. Bei der Anlage neue:r Benutzer:innen wird der erste Eintrag vorgewählt. -administration.configuration.param.feedbackEMail=E-Mail für Feedback -administration.configuration.param.feedbackEMail.description=Wenn diese E-Mail-Adresse angegeben ist, wird ein Feedback-Panel angezeigt, wenn ein Fehler auftritt. Der oder die Anwender:in kann dann eine Feedback-Meldung auslösen (z. B. an ein JIRA-System oder Helpdesk). +administration.configuration.param.excelDateFormats=Datumsformate für Excel +administration.configuration.param.excelDateFormats.description=Diese Datumsformate werden in Excel-Exporten unterstützt. Für jede:n Benutzer:in kann ein Format aus dieser komma-separierten Liste ausgewählt werden. Bei der Anlage neue:r Benutzer:innen wird der erste Eintrag vorgewählt. +administration.configuration.param.feedbackEMail=E-Mail für Feedback +administration.configuration.param.feedbackEMail.description=Wenn diese E-Mail-Adresse angegeben ist, wird ein Feedback-Panel angezeigt, wenn ein Fehler auftritt. Der oder die Anwender:in kann dann eine Feedback-Meldung auslösen (z. B. an ein JIRA-System oder Helpdesk). administration.configuration.param.feedbackEMail.label=Feedback administration.configuration.param.fibu.costConfigured=Kostenkalkulation freigeschaltet? -administration.configuration.param.fibu.costConfigured.description=Wenn die Kostenkalkulation verwendet werden soll (s. Dokumentation) dann muss diese hier grundsätzlich aktiviert werden. Anschließend sind Entitäten wie Projekte und Kunden nutzbar. Bitte beachten: Die Projektentität ist nur sinnvoll, wenn auch eine Kostenträgerrechnung verwendet werden soll. Andernfalls können die Projekte z. B. über den Strukturbaum ab (Kunde -> Projekt -> Release -> Arbeitspakete ...) abgebildet werden. Eine erneutes Ab- und Wiederanmelden ist ggf. erforderlich, damit das Menü aktualisiert wird. +administration.configuration.param.fibu.costConfigured.description=Wenn die Kostenkalkulation verwendet werden soll (s. Dokumentation) dann muss diese hier grundsätzlich aktiviert werden. Anschließend sind Entitäten wie Projekte und Kunden nutzbar. Bitte beachten: Die Projektentität ist nur sinnvoll, wenn auch eine Kostenträgerrechnung verwendet werden soll. Andernfalls können die Projekte z. B. über den Strukturbaum ab (Kunde -> Projekt -> Release -> Arbeitspakete ...) abgebildet werden. Eine erneutes Ab- und Wiederanmelden ist ggf. erforderlich, damit das Menü aktualisiert wird. administration.configuration.param.fibu.defaultVAT=Standardsteuersatz (in %) -administration.configuration.param.fibu.defaultVAT.description=Wird für die Vorbelegung in Formularen verwendet (z. B. Rechnungseingabe). +administration.configuration.param.fibu.defaultVAT.description=Wird für die Vorbelegung in Formularen verwendet (z. B. Rechnungseingabe). administration.configuration.param.hr.emailaddress=HR-E-Mailadresse -administration.configuration.param.hr.emailaddress.description=Mailadresse von HR für Urlaubseinträge +administration.configuration.param.hr.emailaddress.description=Mailadresse von HR für Urlaubseinträge administration.configuration.param.messageOfTheDay=Meldung des Tages -administration.configuration.param.messageOfTheDay.description=Diese Meldung wird im Logindialog angezeigt. Achtung, sie ist auch für nicht autorisierte Besucher sichtbar\! -administration.configuration.param.minPasswordLength=Passwortmindestlänge -administration.configuration.param.minPasswordLength.description=Die Mindestlänge der Benutzer:innenpasswörter. +administration.configuration.param.messageOfTheDay.description=Diese Meldung wird im Logindialog angezeigt. Achtung, sie ist auch für nicht autorisierte Besucher sichtbar\! +administration.configuration.param.minPasswordLength=Passwortmindestlänge +administration.configuration.param.minPasswordLength.description=Die Mindestlänge der Benutzer:innenpasswörter. administration.configuration.param.organization=Organisation administration.configuration.param.organization.description=Der Name der Organisation wird auf PDF-Exports (Zeitberichte oder Monatsberichte) als Titel platziert. administration.configuration.param.password.flag.checkChange=Keine Passwortwiederholung @@ -638,20 +638,20 @@ administration.configuration.param.password.flag.checkChange.description=Ein neu administration.configuration.param.pluginsActivated=Aktivierte Plugins administration.configuration.param.pluginsActivated.description=Liste der aktivierten Plugins. Bitte nicht direkt editieren. administration.configuration.param.systemAdministratorEMail=E-Mail-Adressen der Systemadministratoren -administration.configuration.param.systemAdministratorEMail.description=Im schwerwiegenderen Fehlerfalle versendet ProjectForge eine E-Mail an diese Adresse. Es können mehrere Adressen eingegeben werden, getrennt durch Leerzeichen und Kommata (RFC822). +administration.configuration.param.systemAdministratorEMail.description=Im schwerwiegenderen Fehlerfalle versendet ProjectForge eine E-Mail an diese Adresse. Es können mehrere Adressen eingegeben werden, getrennt durch Leerzeichen und Kommata (RFC822). administration.configuration.param.systemAdministratorEMail.label=Administrator:innen -administration.configuration.param.timesheetTags=Tags für Zeitberichte -administration.configuration.param.timesheetTags.description=Hier können Semikolon-separierte Tags für die Verwendung in Zeitberichten angegeben werden. Diese Tags gelten global. +administration.configuration.param.timesheetTags=Tags für Zeitberichte +administration.configuration.param.timesheetTags.description=Hier können Semikolon-separierte Tags für die Verwendung in Zeitberichten angegeben werden. Diese Tags gelten global. administration.configuration.param.timezone=Standard-Zeitzone -administration.configuration.param.timezone.description=Diese Zeitzone wird standardmäßig verwendet, wenn nichts anderes angegeben ist. Sie wird auch für neue Nutzer:innen voreingestellt. Wenn nichts konfiguriert ist, wird die Zeitzone des Systems (Java VM) verwendet. Diese Zeitzone kann jederzeit geändert werden, da alle Zeitangaben in der Datenbank im unabhängigen UTC-Format gespeichert werden. +administration.configuration.param.timezone.description=Diese Zeitzone wird standardmäßig verwendet, wenn nichts anderes angegeben ist. Sie wird auch für neue Nutzer:innen voreingestellt. Wenn nichts konfiguriert ist, wird die Zeitzone des Systems (Java VM) verwendet. Diese Zeitzone kann jederzeit geändert werden, da alle Zeitangaben in der Datenbank im unabhängigen UTC-Format gespeichert werden. administration.configuration.param.vacation.cal.id=Abwesenheitskalender -administration.configuration.param.vacation.cal.id.description=Der Abwesenheitskalender für Urlaubseinträge +administration.configuration.param.vacation.cal.id.description=Der Abwesenheitskalender für Urlaubseinträge administration.configuration.param.vacation.lastyear.enddate=Endezeitpunkt Vorjahresurlaub -administration.configuration.param.vacation.lastyear.enddate.description=Endezeitpunkt für Urlaub aus Vorjahr (Format: DD.MM.) +administration.configuration.param.vacation.lastyear.enddate.description=Endezeitpunkt für Urlaub aus Vorjahr (Format: DD.MM.) administration.configuration.parameter=Parameter ### not translated: administration.configuration.parameter.pluginsActivated=Activated Plugins ### not translated: administration.configuration.parameter.pluginsActivated.description=List of activated Plugins. Please don't edit directly. -administration.configuration.title.edit=Konfigurationsparameter ändern +administration.configuration.title.edit=Konfigurationsparameter ändern ### not translated: administration.configuration.title.heading=Configuration parameter administration.configuration.title.list=Konfigurationsparameter administration.configuration.value=Wert @@ -659,68 +659,68 @@ administration.configuration.value=Wert ### not translated: administration.missingDatabaseIndicesCreated={0} missing data base indices were successful created. ### not translated: administration.refreshCachesDone=Refresh of caches done for: {0}. administration.reindexFull.successful=Die Voll-Reindizierung war erfolgreich. -administration.reindexNewest.successful=Die Teil-Reindizierung der neuesten Einträge war erfolgreich. +administration.reindexNewest.successful=Die Teil-Reindizierung der neuesten Einträge war erfolgreich. ### not translated: administration.rereadConfiguration=Reread of configuration file config.xml: {0} administration.setup=Einrichten administration.setup.dumpFile=Dumpfile administration.setup.error.import=Beim Import trat ein Fehler auf. In den Protokollen finden sich weitere Informationen. administration.setup.error.uploadfile=Unbekanntes Dateiformat. Diese Datei kann nicht importiert werden. administration.setup.finish=Fertig stellen -administration.setup.heading=Herzlichen Glückwunsch, du hast es fast geschafft\! -administration.setup.heading.subtitle=Danke, dass du ProjectForge gewählt hast. -administration.setup.message.emptyDatabase=Die Datenbank ist nun für den produktiven Einsatz erfolgreich eingerichtet. Viel Spaß\! -administration.setup.message.testdata=Die Datenbank ist nun mit Testdaten erfolgreich angelegt. Viel Spaß\! +administration.setup.heading=Herzlichen Glückwunsch, du hast es fast geschafft\! +administration.setup.heading.subtitle=Danke, dass du ProjectForge gewählt hast. +administration.setup.message.emptyDatabase=Die Datenbank ist nun für den produktiven Einsatz erfolgreich eingerichtet. Viel Spaß\! +administration.setup.message.testdata=Die Datenbank ist nun mit Testdaten erfolgreich angelegt. Viel Spaß\! administration.setup.target=Zielsystem administration.setup.target.emptyDatabase=Produktivsystem -administration.setup.target.emptyDatabase.tooltip=Leere Datenbank (für Produktivsysteme erforderlich) +administration.setup.target.emptyDatabase.tooltip=Leere Datenbank (für Produktivsysteme erforderlich) administration.setup.target.testdata=Testsystem -administration.setup.target.testdata.tooltip=Datenbank mit Testdaten (für Testsysteme) +administration.setup.target.testdata.tooltip=Datenbank mit Testdaten (für Testsysteme) administration.setup.title=Erstanmeldung - ProjectForge einrichten administration.title=Administration -agGrid.sortInfo=Es können mehrere Spalten sortiert werden, indem die Hochstelltaste beim Anklicken von weiteren Spaltenköpfen gedrückt gehalten wird. Außerdem können Spalten umsortiert und in der Breite verändert werden. +agGrid.sortInfo=Es können mehrere Spalten sortiert werden, indem die Hochstelltaste beim Anklicken von weiteren Spaltenköpfen gedrückt gehalten wird. Außerdem können Spalten umsortiert und in der Breite verändert werden. attachment=Anhang -attachment.checksum=Prüfsumme -attachment.encrypt=Verschlüsseln -attachment.encrypt.question=Soll diese Datei nun verschlüsselt werden? Das Passwort wird nicht gespeichert. -attachment.encryption=Verschlüsselung -attachment.expires=Löschfrist +attachment.checksum=Prüfsumme +attachment.encrypt=Verschlüsseln +attachment.encrypt.question=Soll diese Datei nun verschlüsselt werden? Das Passwort wird nicht gespeichert. +attachment.encryption=Verschlüsselung +attachment.expires=Löschfrist attachment.fileId=Dateikennung attachment.fileName=Dateiname -attachment.fileSize=Dateigröße +attachment.fileSize=Dateigröße attachment.info=Info -attachment.list=Anhänge -attachment.onlyAvailableAfterSave=Anhänge können erst hochgeladen werden, nachdem dieses Objekt gespeichert wurde. -attachment.password.info=Dieses Passwort wird ausschließlich zur Verschlüsselung verwendet und nirgends im System gespeichert oder protokolliert. -attachment.showEncryptionOption=Verschlüsselungsoption -attachment.size=Dateigröße -attachment.testDecryption=Entschlüsselung testen -attachment.testDecryption.failed=Entschlüsselung fehlgeschlagen (falsches Passwort oder ungültiges ZIP-Archiv?) -attachment.testDecryption.successful=Entschlüsselung erfolgreich. -attachment.upload.title=Dateien durch Klick auswählen oder Dateien hier hinziehen. -attachment.zip.encrypted=verschlüsselt -attachment.zip.encryptedStandard=verschlüsselt (ZIP-Standard) -attachment.zip.encryptionAlgorithm=Verschlüsselungsalgorithmus -attachment.zip.encrytpedAes128=verschlüsselt (AES-128) -attachment.zip.encrytpedAes256=verschlüsselt (AES-256, hohe Sicherheit) -attachment.zip.standard=ohne Verschlüsselung -attachments=Anhänge +attachment.list=Anhänge +attachment.onlyAvailableAfterSave=Anhänge können erst hochgeladen werden, nachdem dieses Objekt gespeichert wurde. +attachment.password.info=Dieses Passwort wird ausschließlich zur Verschlüsselung verwendet und nirgends im System gespeichert oder protokolliert. +attachment.showEncryptionOption=Verschlüsselungsoption +attachment.size=Dateigröße +attachment.testDecryption=Entschlüsselung testen +attachment.testDecryption.failed=Entschlüsselung fehlgeschlagen (falsches Passwort oder ungültiges ZIP-Archiv?) +attachment.testDecryption.successful=Entschlüsselung erfolgreich. +attachment.upload.title=Dateien durch Klick auswählen oder Dateien hier hinziehen. +attachment.zip.encrypted=verschlüsselt +attachment.zip.encryptedStandard=verschlüsselt (ZIP-Standard) +attachment.zip.encryptionAlgorithm=Verschlüsselungsalgorithmus +attachment.zip.encrytpedAes128=verschlüsselt (AES-128) +attachment.zip.encrytpedAes256=verschlüsselt (AES-256, hohe Sicherheit) +attachment.zip.standard=ohne Verschlüsselung +attachments=Anhänge attachments.short=Anh. birthday=Geburtstag book.abstract=Zusammenfassung book.authors=Autor:innen -book.books=Bücher +book.books=Bücher book.editor=Herausgeber:in -book.error.number=Im Veröffentlichungsjahr sind nur Zahlen erlaubt +book.error.number=Im Veröffentlichungsjahr sind nur Zahlen erlaubt book.error.signatureAlreadyExists=Signatur existiert bereits. -book.error.toFewFields=Für einen Eintrag sollten weitere Felder ausgefüllt werden. +book.error.toFewFields=Für einen Eintrag sollten weitere Felder ausgefüllt werden. book.isbn=ISBN -book.keywords=Schlüsselworte +book.keywords=Schlüsselworte book.lending=Ausleihe book.lendOut=Ausleihen book.lendOutBy=Ausgeliehen von book.lendOutNote=Ausleihnotiz (optional) book.publisher=Verlag -book.returnBook=Zurückgeben +book.returnBook=Zurückgeben book.signature=Signatur book.status.disposed=entsorgt book.status.missed=vermisst @@ -729,28 +729,28 @@ book.status.unknown=unbekannt book.title=Titel book.title.add=Neues Buch book.title.edit=Buch bearbeiten -book.title.heading=Bücher -book.title.list=Bücherliste +book.title.heading=Bücher +book.title.list=Bücherliste book.type=Typ book.type.article=Artikel -book.type.audiobook=Hörbuch +book.type.audiobook=Hörbuch book.type.book=Buch book.type.ebook=E-Book -book.type.film=Film (Datenträger) +book.type.film=Film (Datenträger) book.type.magazine=Magazin book.type.misc=Sonstiges book.type.newspaper=Zeitung book.type.periodical=Periodika -book.type.software=Software (Datenträger) +book.type.software=Software (Datenträger) book.type.thesis=Abschlussarbeit -book.yearOfPublishing=Jahr der Veröffentlichung +book.yearOfPublishing=Jahr der Veröffentlichung book.yearOfPublishing.short=Jahr bookmark.directPageExtendedLink=Direkter Link zu dieser Seite mit Parametern bookmark.directPageExtendedLink.editPage=Direkter Link zu dieser Seite zur Neuanlage mit vorbelegten Feldern bookmark.directPageLink=Direkter Link zu dieser Seite bookmark.title=Seite als Link -button.fileUploadProxy.label=Auswählen -calendar.abonnement.url=URL für Abonnement +button.fileUploadProxy.label=Auswählen +calendar.abonnement.url=URL für Abonnement calendar.allday=ganztags calendar.birthdays.all=Geburtstage calendar.birthdays.favorites=Geburtstage (Adressfavoriten) @@ -768,14 +768,14 @@ calendar.dd.copy.save=Kopieren & Speichern calendar.dd.move.edit=Verschieben & Bearbeiten calendar.dd.move.save=Verschieben & Speichern calendar.defaultCalendar=Standardkalender -calendar.defaultCalendar.tooltip=Der Standardkalender ist der voreingestellte Kalender für neue Einträge. +calendar.defaultCalendar.tooltip=Der Standardkalender ist der voreingestellte Kalender für neue Einträge. calendar.filter.dialog.title=Kalendarfilter -calendar.filter.showCalendarEntries=Kalendereinträge anzeigen +calendar.filter.showCalendarEntries=Kalendereinträge anzeigen calendar.filter.untitled=Unbenannt calendar.filter.vacation.groups=Urlaubskalender Gruppen -calendar.filter.vacation.groups.tooltip=Für Mitglieder dieser Gruppen werden die Urlaube angezeigt. +calendar.filter.vacation.groups.tooltip=Für Mitglieder dieser Gruppen werden die Urlaube angezeigt. calendar.filter.vacation.users=Urlaubskalender Benutzer:in -calendar.filter.vacation.users.tooltip=Für diese Personen werden die Urlaube angezeigt. +calendar.filter.vacation.users.tooltip=Für diese Personen werden die Urlaube angezeigt. calendar.filter.visible=sichtbar calendar.firstDayOfWeek=Wochenanfang calendar.holiday.ascension=Christi Himmelfahrt @@ -785,7 +785,7 @@ calendar.holiday.easterMonday=Ostermontag calendar.holiday.easterSunday=Ostersonntag calendar.holiday.firstXmasDay=1. Weihnachtstag calendar.holiday.goodFriday=Karfreitag -calendar.holiday.maundyThursday=Gründonnerstag +calendar.holiday.maundyThursday=Gründonnerstag calendar.holiday.newYear=Neujahr calendar.holiday.newYearsEve=Silvester calendar.holiday.palmSunday=Palmsonntag @@ -795,7 +795,7 @@ calendar.holiday.shroveTuesday=Faschingsdienstag calendar.holiday.whitMonday=Pfingstmontag calendar.holiday.whitSunday=Pfingstsonntag calendar.holiday.xmasEve=Heiligabend -calendar.icsExport.securityAdvice=Diese URL enthält deinen persönlichen Anmeldeschlüssel für ProjectForge (er ist verschlüsselt und unabhängig von deinem Passwort). Sollte der Verdacht bestehen, dass diese Daten ggf. Dritten vorliegen, so bitte den 'Schlüssel zur Authentifizierung' unter 'Mein Zugang' erneuern. Andernfalls könnten Dritte ebenfalls diesen Kalender abonnieren. +calendar.icsExport.securityAdvice=Diese URL enthält deinen persönlichen Anmeldeschlüssel für ProjectForge (er ist verschlüsselt und unabhängig von deinem Passwort). Sollte der Verdacht bestehen, dass diese Daten ggf. Dritten vorliegen, so bitte den 'Schlüssel zur Authentifizierung' unter 'Mein Zugang' erneuern. Andernfalls könnten Dritte ebenfalls diesen Kalender abonnieren. calendar.month=Monat calendar.month.april=April calendar.month.august=August @@ -804,7 +804,7 @@ calendar.month.february=Februar calendar.month.january=Januar calendar.month.july=Juli calendar.month.june=Juni -calendar.month.march=März +calendar.month.march=März calendar.month.may=Mai calendar.month.november=November calendar.month.october=Oktober @@ -813,15 +813,15 @@ calendar.navigation.today=Heute calendar.newEntry=Neuer Eintrag calendar.option.birthdays=Geburtstage calendar.option.firstHour=Tagesbeginn -calendar.option.firstHour.tooltip=Die erste Stunde, die in der Wochen- und Tagesansicht standardmäßig angezeigt werden soll. Für neue Zeitberichte wird bei Klick auf den Tag dieser Stundenwert als vorgeschlagener Beginn verwendet. +calendar.option.firstHour.tooltip=Die erste Stunde, die in der Wochen- und Tagesansicht standardmäßig angezeigt werden soll. Für neue Zeitberichte wird bei Klick auf den Tag dieser Stundenwert als vorgeschlagener Beginn verwendet. calendar.option.gridSize=Raster in Minuten -calendar.option.gridSize.tooltip=Die Auflösung des Kalenders in der Wochen- und Tagesansicht. +calendar.option.gridSize.tooltip=Die Auflösung des Kalenders in der Wochen- und Tagesansicht. calendar.option.planning=Planung calendar.option.planning.tooltip=Zeigt geplante Projektzeiten, sofern in der Personalplanung vorgenommen. calendar.option.showBreaks=Pausen calendar.option.showBreaks.tooltip=Pausen zwischen den Zeitberichten innerhalb eines Tages anzeigen. calendar.option.slot30=30 -calendar.option.slot30.tooltip=Setzt die Rastergröße des Kalenders auf 30 Minuten (statt 15). +calendar.option.slot30.tooltip=Setzt die Rastergröße des Kalenders auf 30 Minuten (statt 15). calendar.option.statistics=Statistik calendar.option.statistics.tooltip=Zeigt Kalenderwochen und Summen von Zeitberichten pro Tag/Woche an. calendar.option.timesheets=Zeitberichte @@ -842,8 +842,8 @@ calendar.settings.colors.timesheetBreaks=Zeitberichtspausen calendar.settings.colors.timesheets=Zeitberichte calendar.settings.colors.timesheetStats=Zeitberichtsstatistiken calendar.settings.colors.vacations=Urlaube -calendar.settings.colors.vacations.info=Diese Farbe wird für Urlaube verwendet, die in den Ansichtseinstellungen über das Zahnrad ausgewählt wurden.\n\nWenn verschiedene Farben für verschiedene Urlaube von Teams und/oder Personen gewünscht sind, dann kann leicht ein Urlaubskalender für Gruppen/Personen [hier](/react/teamCal/edit) eingerichtet werden. -calendar.settings.intro=Beachte: Die Farben von anderen Kalendern kann einfach durch Klicken auf die Pillen in der Kalenderauswahl oben in der Zeile der Kalenderseite verändert werden. +calendar.settings.colors.vacations.info=Diese Farbe wird für Urlaube verwendet, die in den Ansichtseinstellungen über das Zahnrad ausgewählt wurden.\n\nWenn verschiedene Farben für verschiedene Urlaube von Teams und/oder Personen gewünscht sind, dann kann leicht ein Urlaubskalender für Gruppen/Personen [hier](/react/teamCal/edit) eingerichtet werden. +calendar.settings.intro=Beachte: Die Farben von anderen Kalendern kann einfach durch Klicken auf die Pillen in der Kalenderauswahl oben in der Zeile der Kalenderseite verändert werden. calendar.settings.title=Kalendereinstellungen calendar.shortday.friday=Fr calendar.shortday.monday=Mo @@ -854,20 +854,20 @@ calendar.shortday.tuesday=Di calendar.shortday.wednesday=Mi calendar.showMore=mehr calendar.templates.new=Neuer Kalenderfilter -calendar.templates.new.tooltip=Hier kannst du einen neuen Kalenderfilter unter einem Namen speichern, um schneller zwischen verschiedenen Sichten wechseln zu können. +calendar.templates.new.tooltip=Hier kannst du einen neuen Kalenderfilter unter einem Namen speichern, um schneller zwischen verschiedenen Sichten wechseln zu können. calendar.templates.tooltip=Kalenderfilter verwalten calendar.title=Kalender calendar.today=Heute -calendar.tooltip.selectMonth=Ganzen Monat als Zeitraum wählen -calendar.tooltip.selectNext=Zum nachfolgenden Monat blättern -calendar.tooltip.selectPrevious=Zum vorangehenden Monat blättern -calendar.tooltip.selectWeek=Ganze Woche als Zeitraum wählen -calendar.tooltip.unselectPeriod=Zeitraum löschen +calendar.tooltip.selectMonth=Ganzen Monat als Zeitraum wählen +calendar.tooltip.selectNext=Zum nachfolgenden Monat blättern +calendar.tooltip.selectPrevious=Zum vorangehenden Monat blättern +calendar.tooltip.selectWeek=Ganze Woche als Zeitraum wählen +calendar.tooltip.unselectPeriod=Zeitraum löschen calendar.unit.day=d calendar.unit.hour=h calendar.view.agenda=Agenda calendar.view.oldVersion=Alte Version -calendar.view.oldVersion.tooltip=Die alte Version wird demnächst entfernt. +calendar.view.oldVersion.tooltip=Die alte Version wird demnächst entfernt. calendar.view.overview=Ãœbersicht calendar.view.settings.tooltip=Einstellungen der Ansicht calendar.view.workDays=Werktage @@ -877,21 +877,21 @@ calendar.year=Jahr common.attention=Achtung! common.customized=Angepasst common.import.action.commit=Ãœbernehmen -common.import.action.commit.error.notReconciled=Die Datensätze, die übernommen werden sollen, sind noch nicht abgeglichen, bitte erst abgleichen. -common.import.action.commit.tooltip=Hiermit werden alle selektierten Datensätze in die Datenbank übernommen. -common.import.action.deselectAll=Alle abwählen +common.import.action.commit.error.notReconciled=Die Datensätze, die übernommen werden sollen, sind noch nicht abgeglichen, bitte erst abgleichen. +common.import.action.commit.tooltip=Hiermit werden alle selektierten Datensätze in die Datenbank übernommen. +common.import.action.deselectAll=Alle abwählen common.import.action.downloadValidatedExcel=validatierte Exceldatei common.import.action.reconcile=Abgleichen -common.import.action.reconcile.tooltip=Beim Abgleichen werden alle hochgeladenen Datensätze mit den in der Datenbank bereits vorhandenen Daten verglichen. -common.import.action.select100=Die ersten 100 Einträge auswählen -common.import.action.select500=Die ersten 500 Einträge auswählen -common.import.action.selectAll=Alle auswählen +common.import.action.reconcile.tooltip=Beim Abgleichen werden alle hochgeladenen Datensätze mit den in der Datenbank bereits vorhandenen Daten verglichen. +common.import.action.select100=Die ersten 100 Einträge auswählen +common.import.action.select500=Die ersten 500 Einträge auswählen +common.import.action.selectAll=Alle auswählen common.import.action.showErrorLog=Fehlerprotokoll anzeigen common.import.action.showErrorSummary=Fehlerzusammenfassung anzeigen common.import.action.showInfoLog=Infoprotokoll anzeigen common.import.clearStorage=Leeren -common.import.commitQuestionDialog.heading=Alle ausgewählten Einträge übernehmen? -common.import.commitQuestionDialog.question=Sollen wirklich alle ausgewählten Einträge übernommen werden? Diese Funktion kann nicht wieder rückgängig gemacht werden. +common.import.commitQuestionDialog.heading=Alle ausgewählten Einträge übernehmen? +common.import.commitQuestionDialog.question=Sollen wirklich alle ausgewählten Einträge übernommen werden? Diese Funktion kann nicht wieder rückgängig gemacht werden. common.import.excel.error=Fehler beim Excelimport: {0} in row {1} and column ''{2}''. common.import.excel.error1=Falscher Datentyp in Zeile common.import.excel.error2=Spalte @@ -905,25 +905,25 @@ common.import.status.imported=importiert common.import.status.nothingToDo=nichts zu tun common.import.status.notReconciled=nicht abgeglichen common.import.status.reconciled=abgeglichen -common.import.upload.tooltip=Die Daten werden zunächst zur Prüfung hochgeladen. Insbesondere werden sie nicht direkt in die Datenbank geschrieben. -common.recurrence.frequency.daily=Täglich -common.recurrence.frequency.hourly=Stündlich +common.import.upload.tooltip=Die Daten werden zunächst zur Prüfung hochgeladen. Insbesondere werden sie nicht direkt in die Datenbank geschrieben. +common.recurrence.frequency.daily=Täglich +common.recurrence.frequency.hourly=Stündlich common.recurrence.frequency.monthly=Monatlich common.recurrence.frequency.none=Ohne -common.recurrence.frequency.weekly=Wöchentlich -common.recurrence.frequency.yearly=Jährlich +common.recurrence.frequency.weekly=Wöchentlich +common.recurrence.frequency.yearly=Jährlich common.resultholder.error=Fehler common.resultholder.failed=fehlgeschlagen common.resultholder.ok=OK common.resultholder.warning=Warnung -common.uploadpanel.filetolarge=Die ausgewählte Datei ist zu groß. Die maximale Größe beträgt {0}. -common.uploadpanel.filewrongtype=Die ausgewählte Datei besitzt ein nicht unterstütztes Dateiformat. Unterstützte Formate: {0}. +common.uploadpanel.filetolarge=Die ausgewählte Datei ist zu groß. Die maximale Größe beträgt {0}. +common.uploadpanel.filewrongtype=Die ausgewählte Datei besitzt ein nicht unterstütztes Dateiformat. Unterstützte Formate: {0}. contact.birthday=Geburtstag contact.contacts=Adressen contact.emailValues=E-Mailadressen contact.firstname=Vorname contact.form=Anrede -contact.imValues=Zugänge zu sozialen Medien +contact.imValues=Zugänge zu sozialen Medien contact.name=Nchname contact.phoneValues=Telefonnummern contact.title=Titel @@ -931,13 +931,13 @@ contact.title.add=Neue Adresse contact.title.edit=Adresse bearbeiten ### not translated: contact.title.heading=Address management contact.title.list=Adressliste -contact.type.business=geschäftlich +contact.type.business=geschäftlich contact.type.other=andere contact.type.own=eigene contact.type.postal=postalisch contact.type.private=privat contextMenu.cancel=Abbrechen -contextMenu.newTab=Link in neuem Tab öffnen +contextMenu.newTab=Link in neuem Tab öffnen dialog.title.error=Es ist ein Fehler aufgetreten! dialog.title.information=Information dialog.title.message=Meldung @@ -952,29 +952,29 @@ duration.minutes.one=1 Minute duration.seconds={0} Sekunden duration.seconds.one=1 Sekunde ### not translated: dvelop.title=D-velop -error.date.yearOutOfRange=Das Jahr liegt außerhalb des zulässigen Bereichs: ${minimumYear} - ${maximumYear}. +error.date.yearOutOfRange=Das Jahr liegt außerhalb des zulässigen Bereichs: ${minimumYear} - ${maximumYear}. error.dateInFuture=Datum darf nicht in der Zukunft liegen. error.endDateBeforeBeginDate=Das Endedatum darf nicht vor dem Anfangsdatum liegen. -error.language.unsupported=Sprache wird nicht unterstützt. -error.notAvailableForLoggedInUsers=Diese Funktion ist für angemeldete Benutzer:innen nicht verfügbar. +error.language.unsupported=Sprache wird nicht unterstützt. +error.notAvailableForLoggedInUsers=Diese Funktion ist für angemeldete Benutzer:innen nicht verfügbar. error.posFromDateBeforeFromDate=Das Anfangsdatum einer Position darf nicht vor dem Anfangsdatum des Auftrages liegen. -error.timezone.unsupported=Zeitzone wird nicht unterstützt. -error.yearOutOfRange=Das Jahr liegt außerhalb des zulässigen Bereichs: {0} - {1}. -errorpage.csrfError=Bitte die Aktion wiederholen, da sich der interne Kommunikationsschlüssel geändert hat. Wenn diese Seite überraschend erschienen ist, nachdem z. B. auf einen E-Mail-Link geklickt wurde, so könnte ein möglicher Angriff namens CSRF die Ursache sein. In diesem Falle bitte die Sicherheitsbeauftragten kontaktieren. +error.timezone.unsupported=Zeitzone wird nicht unterstützt. +error.yearOutOfRange=Das Jahr liegt außerhalb des zulässigen Bereichs: {0} - {1}. +errorpage.csrfError=Bitte die Aktion wiederholen, da sich der interne Kommunikationsschlüssel geändert hat. Wenn diese Seite überraschend erschienen ist, nachdem z. B. auf einen E-Mail-Link geklickt wurde, so könnte ein möglicher Angriff namens CSRF die Ursache sein. In diesem Falle bitte die Sicherheitsbeauftragten kontaktieren. errorpage.feedback.description=Fehlerbeschreibung (optional) errorpage.feedback.messageNumber=Fehlernummer errorpage.feedback.placeholder=Bitte hier beschreiben, wie es zum Fehlverhalten kam. errorpage.title=Ein Fehler ist aufgetreten. errorpage.unknownError=Ein interner Fehler ist aufgetreten. -exception.constraintViolation=Es trat eine Verletzung der Datenintegrität in der Datenbank auf. Dies kann daran liegen, dass z. B. der aktuelle Datensatz mit einem anderen (ggf. auch gelöschten) kollidiert. -exception.flowscope.notExists=Flow existiert nicht (vielleicht auf Grund einer Zeitüberschreitung). Bitte nochmals versuchen. +exception.constraintViolation=Es trat eine Verletzung der Datenintegrität in der Datenbank auf. Dies kann daran liegen, dass z. B. der aktuelle Datensatz mit einem anderen (ggf. auch gelöschten) kollidiert. +exception.flowscope.notExists=Flow existiert nicht (vielleicht auf Grund einer Zeitüberschreitung). Bitte nochmals versuchen. exception.internalError=Es ist ein interner Fehler aufgetreten. Dieser wurde protokolliert. exception.luceneParseError=Fehler bei der Vearbeitung des Suchtextes: {0} exception.pleaseContactDeveloperTeam=Interner Fehler, bitte die Entwickler:innen informieren. Die Fehlerkennung in den Fehlerprotokollen lautet #{0}. -exception.scriptError=Fehler bei der Script-Ausführung\: {0} +exception.scriptError=Fehler bei der Script-Ausführung\: {0} feedback.link.tooltip=Feedback senden -feedback.mailSendSuccessful=Eine E-Mail wurde erfolgreich versendet. Vielen Dank für die Mithilfe\! -feedback.receiver=Empfänger:in +feedback.mailSendSuccessful=Eine E-Mail wurde erfolgreich versendet. Vielen Dank für die Mithilfe\! +feedback.receiver=Empfänger:in feedback.send.title=Feedback senden feedback.sender=Sender:in fibu.amountType.all=alle @@ -983,7 +983,7 @@ fibu.amountType.debit=Debit fibu.attachment=Anlagen fibu.auftrag=Auftrag fibu.auftrag.angebot.datum=Angebotsdatum -fibu.auftrag.auftraege=Aufträge +fibu.auftrag.auftraege=Aufträge fibu.auftrag.beauftragungsdatum=Beauftragungsdatum fibu.auftrag.bindungsFrist=Bindungsfrist fibu.auftrag.commissioned=beauftragt @@ -991,21 +991,21 @@ fibu.auftrag.datum=Angebotsdatum fibu.auftrag.datum.short=Datum fibu.auftrag.entscheidung.datum=Entscheidungsdatum fibu.auftrag.erfassung.datum=Erfassungsdatum -fibu.auftrag.error.amountsInPaymentScheduleAreGreaterThanNetSumOfPosition=Die Summe der Beträge von Position {0} im Zahlplan übersteigt die Nettosumme dieser Position. +fibu.auftrag.error.amountsInPaymentScheduleAreGreaterThanNetSumOfPosition=Die Summe der Beträge von Position {0} im Zahlplan übersteigt die Nettosumme dieser Position. fibu.auftrag.error.auftragHatKeinePositionen=Der Auftrag hat keine Auftragspositionen. fibu.auftrag.error.datesInPaymentScheduleNotWithinPeriodOfPerformanceOfPosition=Mindestens ein Datum des Zahlplans zu (den) Position(en) {0} liegt nicht im Leistungszeitraum (plus 3 Monate). -fibu.auftrag.error.invalidPosition=Ungültige Auftragsposition. +fibu.auftrag.error.invalidPosition=Ungültige Auftragsposition. fibu.auftrag.error.nummerBereitsVergeben=Die Auftragsnummer ist bereits vergeben. -fibu.auftrag.error.nummerIstNichtFortlaufend=Die Auftragsnummer ist nicht die nächstfolgende (nicht fortlaufend). -fibu.auftrag.error.nurAbgeschlosseneAuftragsPositionenKoennenVollstaendigFakturiertSein=Nur abgeschlossene Auftragspositionen können vollständig fakturiert sein. -fibu.auftrag.error.vollstaendigFakturiertProtection=Das Flag "vollständig fakturiert" darf nur durch die Buchhaltung manipuliert werden. +fibu.auftrag.error.nummerIstNichtFortlaufend=Die Auftragsnummer ist nicht die nächstfolgende (nicht fortlaufend). +fibu.auftrag.error.nurAbgeschlosseneAuftragsPositionenKoennenVollstaendigFakturiertSein=Nur abgeschlossene Auftragspositionen können vollständig fakturiert sein. +fibu.auftrag.error.vollstaendigFakturiertProtection=Das Flag "vollständig fakturiert" darf nur durch die Buchhaltung manipuliert werden. fibu.auftrag.ersetzen=Ersetzen fibu.auftrag.filter.type.all=alle -fibu.auftrag.filter.type.nochNichtVollstaendigFakturiert=noch nicht vollständig fakturiert -fibu.auftrag.filter.type.vollstaendigFakturiert=vollständig fakturiert +fibu.auftrag.filter.type.nochNichtVollstaendigFakturiert=noch nicht vollständig fakturiert +fibu.auftrag.filter.type.vollstaendigFakturiert=vollständig fakturiert fibu.auftrag.filter.type.zuFakturieren=zu fakturieren fibu.auftrag.forecastExportAsXls=Forecast -fibu.auftrag.forecastExportAsXls.tooltip=Export des Forecasts für die aktuelle Auftragsliste. Das Startdatum wird aus dem Leistungszeitraum Start Filter verwendet. Wenn nicht angegeben wird der 01.01.[aktuelles Jahr] verwendet. +fibu.auftrag.forecastExportAsXls.tooltip=Export des Forecasts für die aktuelle Auftragsliste. Das Startdatum wird aus dem Leistungszeitraum Start Filter verwendet. Wenn nicht angegeben wird der 01.01.[aktuelles Jahr] verwendet. fibu.auftrag.hint.kannVonProjektKundenAbweichen=Kunde kann vom Projektkunden abweichen. fibu.auftrag.invoice.info=, fakturiert: {0}, noch nicht fakturiert: {1} fibu.auftrag.mail.intro=der Auftrag #{0} wurde geändert bzw. angelegt. @@ -1056,14 +1056,14 @@ fibu.auftrag.title.add=Neuer/s Auftrag/Angebot fibu.auftrag.title.edit=Auftrag/Angebot bearbeiten fibu.auftrag.title.heading=Auftragsmanagement fibu.auftrag.title.list=Auftrags- und Angebotsbuch -fibu.auftrag.tooltip.addPaymentschedule=Zahlplanposition hinzufügen -fibu.auftrag.tooltip.addPosition=Auftragsposition hinzufügen +fibu.auftrag.tooltip.addPaymentschedule=Zahlplanposition hinzufügen +fibu.auftrag.tooltip.addPosition=Auftragsposition hinzufügen fibu.auftrag.type=Typ -fibu.auftrag.vollstaendigFakturiert=vollständig fakturiert +fibu.auftrag.vollstaendigFakturiert=vollständig fakturiert fibu.auftrag.zahlung=Zahlung -fibu.buchungssaetze=Buchungssätze +fibu.buchungssaetze=Buchungssätze fibu.buchungssatz.beleg=Beleg -fibu.buchungssatz.error.invalidTimeperiod=Ungültige Zeitraumangabe +fibu.buchungssatz.error.invalidTimeperiod=Ungültige Zeitraumangabe fibu.buchungssatz.gegenKonto=Gegenkonto fibu.buchungssatz.konto=Konto fibu.buchungssatz.menge=Menge @@ -1071,12 +1071,12 @@ fibu.buchungssatz.satznr=Satznummer fibu.buchungssatz.text=Text fibu.buchungssatz.title.add=Neuer Buchungssatz fibu.buchungssatz.title.edit=Buchungssatz bearbeiten -fibu.buchungssatz.title.heading=Buchungssätze -fibu.buchungssatz.title.list=Liste der Buchungssätze +fibu.buchungssatz.title.heading=Buchungssätze +fibu.buchungssatz.title.list=Liste der Buchungssätze fibu.businessAssessment=BWA fibu.businessAssessment.merchandisePurchase=Mat./Wareneinkauf fibu.businessAssessment.overallPerformance=Gesamtleistung -fibu.businessAssessment.preliminaryResult=Vorläufiges Ergebnis +fibu.businessAssessment.preliminaryResult=Vorläufiges Ergebnis fibu.common.assignedPersons=PL/BUL/KAM/AP fibu.common.betrag=Betrag fibu.common.brutto=Brutto @@ -1120,13 +1120,13 @@ fibu.employee.error.employeWithUserExists=Es existiert bereits ein:e Mitarbeiter fibu.employee.iban=IBAN fibu.employee.iststunden=Iststunden fibu.employee.panel.error.employeeNotFound=Mitarbeiter:in nicht gefunden. -fibu.employee.salaries=Gehälter +fibu.employee.salaries=Gehälter fibu.employee.salary=Gehaltszahlung fibu.employee.salary.agAnteil=AG-Anteil fibu.employee.salary.bruttoMitAgAnteil=Brutto mit AG-Anteil -fibu.employee.salary.error.monthNotGiven=Ein Monat muss ausgewählt werden. -fibu.employee.salary.error.salaryAlreadyExist=Das Monatsgehalt für diese:n Mitarbeiter:in existiert bereits. -fibu.employee.salary.exportXls.tooltip=Gehaltsexport mit Kostenträgerzuweisungen gemäß der Zeitberichtsbuchungen. +fibu.employee.salary.error.monthNotGiven=Ein Monat muss ausgewählt werden. +fibu.employee.salary.error.salaryAlreadyExist=Das Monatsgehalt für diese:n Mitarbeiter:in existiert bereits. +fibu.employee.salary.exportXls.tooltip=Gehaltsexport mit Kostenträgerzuweisungen gemäß der Zeitberichtsbuchungen. fibu.employee.salary.id=Gehalts-ID fibu.employee.salary.title.add=Neue Gehaltszahlung fibu.employee.salary.title.edit=Gehaltszahlung bearbeiten @@ -1134,7 +1134,7 @@ fibu.employee.salary.title.heading=Gehaltszahlungen fibu.employee.salary.title.list=Gehaltsliste fibu.employee.salary.type=Zahlungstyp fibu.employee.salary.type.gehalt=Gehalt -fibu.employee.salary.type.praemie=Prämie +fibu.employee.salary.type.praemie=Prämie fibu.employee.salary.type.sonderzahlung=Sonderzahlung fibu.employee.salary.type.tantieme=Tantieme fibu.employee.salary.type.zielvereinbarung=Zielvereinbarung @@ -1150,7 +1150,7 @@ fibu.employee.status.freelancer=Freelancer:in fibu.employee.status.praktikant=Praktikant:in fibu.employee.status.studentischeAbschlussarbeit=Studentische Abschlussarbeit fibu.employee.status.studentischeHilfskraft=Studentische Hilfskraft -fibu.employee.street=Straße +fibu.employee.street=Straße fibu.employee.title.add=Neue:r Mitarbeiter:in fibu.employee.title.edit=Mitarbeiter:in bearbeiten fibu.employee.title.heading=Mitarbeiter:in @@ -1165,7 +1165,7 @@ fibu.fakturiert.not=nicht fakturiert fibu.headOfBusinessManager=Management Business Unit fibu.konto=Konto fibu.konto.bezeichnung=Bezeichnung -fibu.konto.error.invalidKonto=Ungültiges Konto. +fibu.konto.error.invalidKonto=Ungültiges Konto. fibu.konto.konten=Konten fibu.konto.nummer=Kontonummer fibu.konto.status.active=aktiv @@ -1175,9 +1175,9 @@ fibu.konto.title.edit=Konto bearbeiten fibu.konto.title.heading=Buchungskonten fibu.konto.title.list=Liste der Konten fibu.konto.validate.duplicate=Ein Konto mit der angegeben Nummer existiert bereits -fibu.kost.error.collision=Kollision mit bereits vorhandenem Kostenträger. -fibu.kost.error.invalidKost=Ungültiger Kostenträger. -fibu.kost.kostentraeger=Kostenträger +fibu.kost.error.collision=Kollision mit bereits vorhandenem Kostenträger. +fibu.kost.error.invalidKost=Ungültiger Kostenträger. +fibu.kost.kostentraeger=Kostenträger fibu.kost.reporting=Reporting fibu.kost.reporting.clearStorage=Leeren fibu.kost.reporting.createReport=Report erstellen @@ -1189,16 +1189,16 @@ fibu.kost1=Kost1 fibu.kost1.art=Art fibu.kost1.id=Kost1-ID fibu.kost1.kost1s=Kost1s -fibu.kost1.list.select.title=Kost1 wählen +fibu.kost1.list.select.title=Kost1 wählen fibu.kost1.number=Kost1-Nummer fibu.kost1.title.add=Neue Kost1 fibu.kost1.title.edit=Kost1 bearbeiten fibu.kost1.title.heading=Kost1 fibu.kost1.title.list=Liste der Kost1s -fibu.kost1.title.list.select=Kost1 wählen +fibu.kost1.title.list.select=Kost1 wählen fibu.kost2=Kost2 fibu.kost2.art=Art -fibu.kost2.error.projektNeededForNummernkreis=Das Projekt muss für die Nummernkreise 4 und 5 zwingend angegeben sein. +fibu.kost2.error.projektNeededForNummernkreis=Das Projekt muss für die Nummernkreise 4 und 5 zwingend angegeben sein. fibu.kost2.id=Kost2-ID fibu.kost2.kost2s=Kost2s fibu.kost2.number=Kost2-Nummer @@ -1206,7 +1206,7 @@ fibu.kost2.title.add=Neue Kost2 fibu.kost2.title.edit=Kost2 bearbeiten fibu.kost2.title.heading=Kost2 fibu.kost2.title.list=Liste der Kost2s -fibu.kost2.title.list.select=Kost2 wählen +fibu.kost2.title.list.select=Kost2 wählen fibu.kost2.workFraction=Arbeitszeitanteil fibu.kost2art.error.notFound=Kost2-Art existiert nicht. fibu.kost2art.kost2arten=Kost2-Arten @@ -1223,10 +1223,10 @@ fibu.kunde.division=Bereich fibu.kunde.identifier=Kurzbezeichner fibu.kunde.konto.tooltip=Dieses Konto wird bei einem Export von Debitorenrechnungen mit ausgegeben, sofern im konkreten Projekt, welches der Rechnung evtl. zugewiesen ist, kein anderes Konto angegeben ist. fibu.kunde.kunden=Kunden -fibu.kunde.list.select.title=Kunde wählen +fibu.kunde.list.select.title=Kunde wählen fibu.kunde.name=Kundenname fibu.kunde.nummer=Kundennummer -fibu.kunde.pleaseSelectKunde=Bitte Kunde wählen +fibu.kunde.pleaseSelectKunde=Bitte Kunde wählen fibu.kunde.select=Auswahl Kunde fibu.kunde.status.acquisition=Akquise fibu.kunde.status.active=aktiv @@ -1238,18 +1238,18 @@ fibu.kunde.title.add=Neuer Kunde fibu.kunde.title.edit=Kunde bearbeiten fibu.kunde.title.heading=Kunden fibu.kunde.title.list=Kunden -fibu.kunde.title.list.select=Kunde auswählen +fibu.kunde.title.list.select=Kunde auswählen fibu.kunde.validation.existingCustomerNr=Es existiert bereits ein Kunde mit dieser Kundennummer -fibu.kunde.wizard.notYetAvailable=Der Assistent ist noch nicht verfügbar. Bitte beachten\: Kundenobjekte werden aktuell nur und nur für Kostenträgerrechnungen benötigt (s. Dokumentation). Bitte benutzen Sie den Strukturbaum, um eine Kunden-Projekt-Hierarchie abzubilden. +fibu.kunde.wizard.notYetAvailable=Der Assistent ist noch nicht verfügbar. Bitte beachten\: Kundenobjekte werden aktuell nur und nur für Kostenträgerrechnungen benötigt (s. Dokumentation). Bitte benutzen Sie den Strukturbaum, um eine Kunden-Projekt-Hierarchie abzubilden. fibu.kundeProjekt=Kunde/Projekt -fibu.modeOfPayment.type.annual=jährlich +fibu.modeOfPayment.type.annual=jährlich fibu.modeOfPayment.type.fixed=fix fibu.modeOfPayment.type.monthly=monatlich fibu.modeOfPayment.type.quarterly=quartalsweise fibu.monthlyEmployeeReport.daysCountWithoutTimesheets=Anzahl AT ohne Zeitberichte fibu.monthlyEmployeeReport.daysWithoutTimesheets=AT ohne Zeitberichte fibu.monthlyEmployeeReport.totalSum=Bruttoarbeitszeit -fibu.monthlyEmployeeReport.totalSum.tooltip=Die Bruttoarbeitszeit kann von der Summe abweichen, wenn für bestimmte Kostenarten nur ein Anteil als Arbeitszeit gerechnet wird. +fibu.monthlyEmployeeReport.totalSum.tooltip=Die Bruttoarbeitszeit kann von der Summe abweichen, wenn für bestimmte Kostenarten nur ein Anteil als Arbeitszeit gerechnet wird. fibu.monthlyEmployeeReport.withoutTimesheets=ohne Zeitberichte fibu.notYetInvoiced=noch nicht fakturiert fibu.payment.type=Zahlungsart @@ -1273,13 +1273,13 @@ fibu.projekt=Projekt fibu.projekt.edit.kost2DoesAlreadyExists=Kost2 existiert bereits. fibu.projekt.identifier=Kurzbezeichner fibu.projekt.internKost2_4=Intern: Koststellen 2-4 -fibu.projekt.konto.tooltip=Wird für den Export von Debitorenrechnungen verwendet. Wenn kein Konto angegeben, wird ein evtl. vorhandenes Konto des diesem Projekt zugeordneten Kundens verwendet. +fibu.projekt.konto.tooltip=Wird für den Export von Debitorenrechnungen verwendet. Wenn kein Konto angegeben, wird ein evtl. vorhandenes Konto des diesem Projekt zugeordneten Kundens verwendet. fibu.projekt.leiter=Projektmanagement -fibu.projekt.list.select.title=Projekt wählen +fibu.projekt.list.select.title=Projekt wählen fibu.projekt.multiselected.title=Mehrfachauswahl Projekte fibu.projekt.name=Name fibu.projekt.nummer=Nummer -fibu.projekt.pleaseSelectProjekt=Bitte Projekt wählen +fibu.projekt.pleaseSelectProjekt=Bitte Projekt wählen fibu.projekt.projekte=Projekte fibu.projekt.projektManagerGroup=Projektmanagergruppe fibu.projekt.status.acquisition=Akquise @@ -1287,15 +1287,15 @@ fibu.projekt.status.build=Entwicklungsphase fibu.projekt.status.ended=beendet fibu.projekt.status.maintenance=Wartung fibu.projekt.status.none=Nicht gesetzt. -fibu.projekt.status.onhold=vorrübergehend (noch) nicht aktiv +fibu.projekt.status.onhold=vorrübergehend (noch) nicht aktiv fibu.projekt.status.productive=produktiv (ohne Wartung) fibu.projekt.title.add=Neues Projekt fibu.projekt.title.edit=Projekt bearbeiten fibu.projekt.title.heading=Projekte fibu.projekt.title.list=Projekte -fibu.projekt.title.list.select=Projekt auswählen -fibu.projekt.validation.numbernotfreeforcustomer=Nummer bereits für anderes Kundenprojekt vergeben -fibu.projekt.wizard.notYetAvailable=Der Assistent ist noch nicht verfügbar. Bitte beachten\: Projektobjekte werden aktuell nur und nur für Kostenträgerrechnungen benötigt (s. Dokumentation). Bitte benutzen Sie den Strukturbaum, um eine Kunden-Projekt-Hierarchie abzubilden. +fibu.projekt.title.list.select=Projekt auswählen +fibu.projekt.validation.numbernotfreeforcustomer=Nummer bereits für anderes Kundenprojekt vergeben +fibu.projekt.wizard.notYetAvailable=Der Assistent ist noch nicht verfügbar. Bitte beachten\: Projektobjekte werden aktuell nur und nur für Kostenträgerrechnungen benötigt (s. Dokumentation). Bitte benutzen Sie den Strukturbaum, um eine Kunden-Projekt-Hierarchie abzubilden. fibu.rechnung=Debitorenrechnung fibu.rechnung.beschreibung=Beschreibung fibu.rechnung.besonderheiten=Besonderheiten @@ -1308,38 +1308,38 @@ fibu.rechnung.customernr=Kundennummer fibu.rechnung.datum=Rechnungsdatum fibu.rechnung.datum.short=Datum fibu.rechnung.discount=Skonto (Datum und %-Wert) -fibu.rechnung.discountMaturity=Skonto Fälligkeit +fibu.rechnung.discountMaturity=Skonto Fälligkeit fibu.rechnung.discountPercent=Skonto Prozent -fibu.rechnung.error.bezahlDatumRequired=Wenn der Zahlbetrag gegeben ist muss auch das zugehörige Bezahldatum eingegeben sein. +fibu.rechnung.error.bezahlDatumRequired=Wenn der Zahlbetrag gegeben ist muss auch das zugehörige Bezahldatum eingegeben sein. fibu.rechnung.error.gutschriftsanzeigeDarfKeineRechnungsnummerHaben=Eine Gutschriftsanzeige durch den Kunden hat keine Rechnungsnummer. -fibu.rechnung.error.kundeTextOderProjektRequired=Ein Kundentext oder ein Projekt müssen angegeben sein. +fibu.rechnung.error.kundeTextOderProjektRequired=Ein Kundentext oder ein Projekt müssen angegeben sein. fibu.rechnung.error.negativAmount=Bei Ãœberweisung kann kein negativer Brutto-Betrag existieren fibu.rechnung.error.rechnungHatKeinePositionen=Die Rechnung hat keine Rechnungspositionen. fibu.rechnung.error.rechnungsNummerBereitsVergeben=Die Rechnungsnummer ist bereits vergeben. -fibu.rechnung.error.rechnungsNummerIstNichtFortlaufend=Die Rechnungsnummer ist nicht die nächstfolgende (nicht fortlaufend). +fibu.rechnung.error.rechnungsNummerIstNichtFortlaufend=Die Rechnungsnummer ist nicht die nächstfolgende (nicht fortlaufend). fibu.rechnung.error.statusBezahltErfordertZahlBetrag=Die Rechnung kann nur auf "bezahlt" gesetzt werden, wenn auch ein Zahlbetrag eingetragen wurde. -fibu.rechnung.error.zahlbetragRequired=Wenn das Bezahldatum gegeben ist muss auch der zugehörige Zahlbetrag eingegeben sein. +fibu.rechnung.error.zahlbetragRequired=Wenn das Bezahldatum gegeben ist muss auch der zugehörige Zahlbetrag eingegeben sein. fibu.rechnung.exportInvoice=Rechnung exportieren -fibu.rechnung.faelligkeit=Fälligkeit -fibu.rechnung.faelligkeit.short=Fällig -fibu.rechnung.filter.ueberfaellig=überfällig +fibu.rechnung.faelligkeit=Fälligkeit +fibu.rechnung.faelligkeit.short=Fällig +fibu.rechnung.filter.ueberfaellig=überfällig fibu.rechnung.filter.unbezahlt=unbezahlt -fibu.rechnung.hint.kannVonProjektKundenAbweichen=Rechnungsempfänger kann vom Projektkunden abweichen. +fibu.rechnung.hint.kannVonProjektKundenAbweichen=Rechnungsempfänger kann vom Projektkunden abweichen. fibu.rechnung.iban=IBAN -fibu.rechnung.konto.tooltip=Wird für den Export von Debitorenrechnungen verwendet. Wenn kein Konto angegeben, wird ein evtl. vorhandenes Konto des dieser Rechnung zugeordneten Projekts bzw. Kundens verwendet. +fibu.rechnung.konto.tooltip=Wird für den Export von Debitorenrechnungen verwendet. Wenn kein Konto angegeben, wird ein evtl. vorhandenes Konto des dieser Rechnung zugeordneten Projekts bzw. Kundens verwendet. fibu.rechnung.kostExcelExport=Kostenzuweisungen exportieren -fibu.rechnung.kostExcelExport.tootlip=Excel-Export aller Rechnungen für den gewählten Zeitraum im Excel-Format inklusive der Kostenträgerzuweisungen. +fibu.rechnung.kostExcelExport.tootlip=Excel-Export aller Rechnungen für den gewählten Zeitraum im Excel-Format inklusive der Kostenträgerzuweisungen. fibu.rechnung.kostZuweisungFehlbetrag=Kostzuweisungfehlbetrag fibu.rechnung.mehrwertSteuerSatz=Mwst-Satz fibu.rechnung.menge=Menge fibu.rechnung.mitSkonto=mit Skonto -fibu.rechnung.multiselected.info=Wenn das Bezahldatum ausgewählt ist werden alle Einträge auf bezahlt gesetzt. Der Bezahltbetrag wird ebenfalls gesetzt. Wenn der Betrag gelöscht werden soll, wird auch der Zahlbetrag gelöscht. +fibu.rechnung.multiselected.info=Wenn das Bezahldatum ausgewählt ist werden alle Einträge auf bezahlt gesetzt. Der Bezahltbetrag wird ebenfalls gesetzt. Wenn der Betrag gelöscht werden soll, wird auch der Zahlbetrag gelöscht. fibu.rechnung.multiselected.title=Mehrfachauswahl Rechnungen fibu.rechnung.nummer=Nummer fibu.rechnung.nummer.short=Nr. fibu.rechnung.offen=Offen fibu.rechnung.position.einzelNetto=Einzelnettopreis -fibu.rechnung.receiver=Empfänger +fibu.rechnung.receiver=Empfänger fibu.rechnung.rechnungen=Debitorenrechnungen fibu.rechnung.showEditableKostZuweisungen=Kostzuweisungen editieren fibu.rechnung.showKostZuweisungen=Kostzuweisungen @@ -1359,11 +1359,11 @@ fibu.rechnung.title.add=Neue Debitorenrechnung fibu.rechnung.title.edit=Debitorenrechnung bearbeiten fibu.rechnung.title.heading=Debitorenrechnungen fibu.rechnung.title.list=Debitorenrechnungsliste -fibu.rechnung.tooltip.addPosition=Rechnungsposition hinzufügen +fibu.rechnung.tooltip.addPosition=Rechnungsposition hinzufügen fibu.rechnung.transferExport=Ãœberweisungen exportieren -fibu.rechnung.transferExport.error=Während des Exportes der Ãœberweisung ist ein unerwarteter Fehler aufgetreten. -fibu.rechnung.transferExport.error.entries=Export aufgrund fehlender oder ungültiger Angaben in den folgenden Rechnungen nicht möglich: {0}. -fibu.rechnung.transferExport.error.missing=Export aufgrund fehlender oder ungültiger Angaben in den folgenden Feldern nicht möglich: {0}. +fibu.rechnung.transferExport.error=Während des Exportes der Ãœberweisung ist ein unerwarteter Fehler aufgetreten. +fibu.rechnung.transferExport.error.entries=Export aufgrund fehlender oder ungültiger Angaben in den folgenden Rechnungen nicht möglich: {0}. +fibu.rechnung.transferExport.error.missing=Export aufgrund fehlender oder ungültiger Angaben in den folgenden Feldern nicht möglich: {0}. fibu.rechnung.transferExport.tootlip=Export der Ãœberweisung dieser Rechnung im pain.001.003.03-Format. fibu.rechnung.typ=Typ fibu.rechnung.typ.cancellation=Storno @@ -1372,7 +1372,7 @@ fibu.rechnung.typ.rechnung=Rechnung fibu.rechnung.zahlBetrag=Zahlbetrag fibu.rechnung.zahlBetrag.short=Bezahlt fibu.rechnung.zahlungsZiel=Zahlungsziel -fibu.rechnung.zahlungsZiel.actual=Tatsächliches Zahlungsziel +fibu.rechnung.zahlungsZiel.actual=Tatsächliches Zahlungsziel fibu.rechnungen=Debitorenrechnungen fibu.reporting.duplicates=Dubletten fibu.reporting.other=Sonstige @@ -1382,16 +1382,16 @@ fibu.title.fakturiert=Fakturiert fibu.title.fakturiert.not=Nicht fakturiert fibu.toBeInvoiced=zu fakturieren fibu.tooltip.nummerWirdAutomatischVergeben=Nummer wird automatisch vergeben. -fibu.tooltip.selectKost1=Kost1 wählen -fibu.tooltip.selectKost2=Kost2 wählen -fibu.tooltip.selectKunde=Kunde wählen -fibu.tooltip.selectProjekt=Projekt wählen +fibu.tooltip.selectKost1=Kost1 wählen +fibu.tooltip.selectKost2=Kost2 wählen +fibu.tooltip.selectKunde=Kunde wählen +fibu.tooltip.selectProjekt=Projekt wählen fibu.tooltip.unselectKost1=Kost1-Auswahl aufheben fibu.tooltip.unselectKost2=Kost2-Auswahl aufheben fibu.tooltip.unselectKunde=Kundenauswahl aufheben fibu.tooltip.unselectProjekt=Projektauswahl aufheben -file.panel.deleteExistingFile.heading=Datei wirklich löschen? -file.panel.deleteExistingFile.question=Soll die vorhandene Datei wirklich gelöscht bzw. überschrieben werden? +file.panel.deleteExistingFile.heading=Datei wirklich löschen? +file.panel.deleteExistingFile.question=Soll die vorhandene Datei wirklich gelöscht bzw. überschrieben werden? finance.accountingRecord.dc=SH finance.accountingRecord.dc.credit=Haben finance.accountingRecord.dc.debit=Soll @@ -1403,17 +1403,17 @@ form.ajaxEditableLabel.tooltip=Zum Editieren bitte anklicken. gantt.access.all=Alle gantt.access.owner=Eigner:in gantt.access.projectmanager=Projektmanager -gantt.action.move=Verschieben (Unteraktivität) -gantt.action.moveToTop=Verschieben (als Hauptaktivität) -gantt.action.newActivity=Neue Aktivität -gantt.contextMenu.newSubActivity=Neue Unteraktivität +gantt.action.move=Verschieben (Unteraktivität) +gantt.action.moveToTop=Verschieben (als Hauptaktivität) +gantt.action.newActivity=Neue Aktivität +gantt.contextMenu.newSubActivity=Neue Unteraktivität gantt.contextMenu.saveAsTask=Als ProjectForge-Strukturelement anlegen gantt.contextMenu.setInvisible=Unsichtbar machen gantt.contextMenu.setSubTasksVisible=Sichtbar inkl. Untertasks gantt.duration=Dauer gantt.endDate=Endedatum gantt.error.durationAndEndDateAreMutuallyExclusive=Es kann entweder nur die Dauer oder nur ein Endedatum angegeben werden. -gantt.error.parentObjectIsNotAPFTask=Das übergeordnete Gantt-Objekt muss ein ProjectForge-Strukturelement sein\! +gantt.error.parentObjectIsNotAPFTask=Das übergeordnete Gantt-Objekt muss ein ProjectForge-Strukturelement sein\! gantt.export.jpg=JPG (nicht empfohlen) gantt.export.msproject.mpx=Microsoft® Project (MPX) gantt.export.msproject.xml=Microsoft® Project (XML) @@ -1423,7 +1423,7 @@ gantt.export.projectforge=ProjectForge (XML) gantt.export.svg=SVG gantt.name=Name gantt.objectType=Gantt-Objekttyp -gantt.objectType.activity=Aktivität (Standard) +gantt.objectType.activity=Aktivität (Standard) gantt.objectType.activity.short=Std gantt.objectType.milestone=Meilenstein gantt.objectType.milestone.short=MS @@ -1431,11 +1431,11 @@ gantt.objectType.short=Typ gantt.objectType.summary=Zusammenfassung gantt.objectType.summary.short=Zus gantt.owner=Eigner -gantt.predecessor=Vorgänger -gantt.predecessor.paste=Als Vorgänger einfügen +gantt.predecessor=Vorgänger +gantt.predecessor.paste=Als Vorgänger einfügen gantt.predecessorOffset=Offset -gantt.question.moveTask=Soll das zugehörige ProjectForge-Strukturelement wirklich verschoben werden? -gantt.question.saveGanttObjectAsTask=Soll dieses Gantt-Objekt wirklich als neues ProjectForge-Strukturelement übernommen werden? +gantt.question.moveTask=Soll das zugehörige ProjectForge-Strukturelement wirklich verschoben werden? +gantt.question.saveGanttObjectAsTask=Soll dieses Gantt-Objekt wirklich als neues ProjectForge-Strukturelement übernommen werden? gantt.relationType=Beziehung gantt.relationType.finish_finish=Ende-Ende gantt.relationType.finish_finish.short=EE @@ -1459,8 +1459,8 @@ gantt.title.edit=Ganttdiagramm bearbeiten gantt.title.heading=Ganttdiagramme gantt.title.list=Ganttdiagramme gantt.tooltip.isVisible=Soll dieses Objekt angezeigt werden? -gantt.tooltip.rejectValue=Diesen Wert verwerfen und den Wert des Strukturelements übernehmen\: ''{0}''. -gantt.tooltip.returnKeyCallsRedraw=Drücken Sie die Eingabetaste, um das Diagramm erneut zu zeichnen. +gantt.tooltip.rejectValue=Diesen Wert verwerfen und den Wert des Strukturelements übernehmen\: ''{0}''. +gantt.tooltip.returnKeyCallsRedraw=Drücken Sie die Eingabetaste, um das Diagramm erneut zu zeichnen. gantt.tooltip.saveTaskValue=Diesen Wert dem Strukturelement zuweisen und abspeichern. gantt.x.unit.auto=Automatisch gantt.x.unit.day=Tag @@ -1471,32 +1471,32 @@ group.assignedUsers=Assoziierte Benutzer:in group.error.groupnameAlreadyExists=Gruppenname ist bereits vergeben. group.groups=Gruppen group.ldapValues=LDAP -group.list.select.title=Gruppe wählen +group.list.select.title=Gruppe wählen group.localGroup=Lokale Gruppe group.localGroup.not=Allgemeine Gruppe group.localGroup.tooltip=Lokale Gruppen werden nicht mit einem externen Benutzer:innensystem synchronisiert (z. B. LDAP). group.nestedGroups=Eingebettete Gruppen group.nestedGroupsAllowed=Eingebettete Gruppen erlaubt? -group.nestedGroupsAllowed.tooltip=Zur Sicherheit sollten für die Gruppen Administrator:innen, Controlling etc. keine eingebetteten Gruppen erlaubt sein, um versehentlichen unbefugten Zugriff zu verhindern. +group.nestedGroupsAllowed.tooltip=Zur Sicherheit sollten für die Gruppen Administrator:innen, Controlling etc. keine eingebetteten Gruppen erlaubt sein, um versehentlichen unbefugten Zugriff zu verhindern. group.owner=Gruppenbesitzer -group.pleaseSelectGroup=Bitte Gruppe wählen +group.pleaseSelectGroup=Bitte Gruppe wählen group.systemGroups=Systemgruppen group.title.add=Neue Gruppe group.title.edit=Gruppe bearbeiten group.title.heading=Benutzergruppen group.title.list=Gruppenliste -group.title.list.select=Gruppen wählen +group.title.list.select=Gruppen wählen group.unassignedUsers=Nicht assoziierte Benutzer:innen -hint.selectMode.quickselect=Beachte den Quick-Select\: Enthält das Suchergebnis nur einen einzelnen Eintrag, so wird dieser automatisch übernommen. +hint.selectMode.quickselect=Beachte den Quick-Select\: Enthält das Suchergebnis nur einen einzelnen Eintrag, so wird dieser automatisch übernommen. history.entryType=Aktion history.newValue=Neuer Wert history.oldValue=Alter Wert history.propertyName=Feld history.was=war -hr.planning.description=Tätigkeit -hr.planning.entry.copyFromPredecessor=Vorgänger kopieren -hr.planning.entry.error.entryDoesAlreadyExistForUserAndWeekOfYear=Dieser Eintrag kollidiert mit einem bereits vorhandenem für den gleichen Benutzer für die gleiche Kalenderwoche. -hr.planning.entry.error.noRightForProject=Es fehlt das Recht, Einträge für das Projekt ''{0}'' zu erstellen oder zu ändern +hr.planning.description=Tätigkeit +hr.planning.entry.copyFromPredecessor=Vorgänger kopieren +hr.planning.entry.error.entryDoesAlreadyExistForUserAndWeekOfYear=Dieser Eintrag kollidiert mit einem bereits vorhandenem für den gleichen Benutzer für die gleiche Kalenderwoche. +hr.planning.entry.error.noRightForProject=Es fehlt das Recht, Einträge für das Projekt ''{0}'' zu erstellen oder zu ändern hr.planning.entry.error.statusAndProjektNotAllowed=Es darf nur der Status oder das Projekt gesetzt sein. hr.planning.entry.error.statusOrProjektRequired=Es muss der Status oder das Projekt gesetzt sein. hr.planning.entry.status.absence=Abwesenheit @@ -1513,7 +1513,7 @@ hr.planning.hours=Stunden hr.planning.notPlanned=Ungeplant hr.planning.period=Zeitraum hr.planning.plannings=Personaleinsatzplanung -hr.planning.priority=Priorität +hr.planning.priority=Priorität hr.planning.probability=Wahrscheinlichkeit hr.planning.probability.short=% hr.planning.sum=Summe @@ -1521,52 +1521,52 @@ hr.planning.till=bis hr.planning.title.add=Neue Resourcenplanung hr.planning.title.edit=Resourcenplanung bearbeiten hr.planning.title.heading=Resourcenplanung -hr.planning.title.list=Resourcenplanungsübersicht -hr.planning.tooltip.addEntry=Eintrag hinzufügen +hr.planning.title.list=Resourcenplanungsübersicht +hr.planning.tooltip.addEntry=Eintrag hinzufügen hr.planning.total=Gesamt hr.planning.unassignedHours=ohne Tagangabe -hr.planning.view.title=Planungsübersicht +hr.planning.view.title=Planungsübersicht hr.planning.weekend=Wochenende hr.planning.workdays=Arbeitstage -import.confirmMessage=Sollen nun alle ausgewählten Einträge importiert werden? Diese Aktion kann nicht rückgängig gemacht werden. +import.confirmMessage=Sollen nun alle ausgewählten Einträge importiert werden? Diese Aktion kann nicht rückgängig gemacht werden. import.display.options=Anzeigeoptionen import.entry.error=Fehler -import.entry.status.deleted=Gelöscht +import.entry.status.deleted=Gelöscht import.entry.status.faulty=Fehlerhaft import.entry.status.modified=Modifiziert import.entry.status.new=Neu import.entry.status.unknown=Unbekannt import.entry.status.unknown_modification=Unbekannt -import.entry.status.unmodified=Unverändert -import.error.noEntrySelected=Kein Eintrag zum Importieren ausgewählt. +import.entry.status.unmodified=Unverändert +import.error.noEntrySelected=Kein Eintrag zum Importieren ausgewählt. import.error.nothingToImport=Nichts zum Importieren gefunden. Bitte eine andere Datei probieren oder die Importeinstellungen entsprechend definieren. import.field.mapping=Feldzuordnung import.field.name=Feldname -import.help.settings.info=Es können Importeinstellungen wie Encoding (Zeichensatz) und Feldzuordnungen sowie Datenformatee definiert werden. Hier ein Beispiel als Kopiervorlage: +import.help.settings.info=Es können Importeinstellungen wie Encoding (Zeichensatz) und Feldzuordnungen sowie Datenformatee definiert werden. Hier ein Beispiel als Kopiervorlage: import.help.settings.title=Tipps import.info.detectedColumns=Erkannte Spalten import.info.unknownColumns=Unbekannte Spalten -import.result.numberOfCreated=Anzahl neu angelegter Einträge -import.result.numberOfDeleted=Anzahl gelöschter Einträge -import.result.numberOfUnmodified=Anzahl unveränderter Einträge -import.result.numberOfUpdated=Anzahl geänderter Einträge +import.result.numberOfCreated=Anzahl neu angelegter Einträge +import.result.numberOfDeleted=Anzahl gelöschter Einträge +import.result.numberOfUnmodified=Anzahl unveränderter Einträge +import.result.numberOfUpdated=Anzahl geänderter Einträge import.result.title=Importergebnis -import.stats.deleted=Anzahl zu löschender Einträge -import.stats.faulty=Anzahl fehlerhafter Einträge (s. Statustooltip) -import.stats.modified=Anzahl zu ändernder Einträge -import.stats.new=Anzahl neuer Einträge +import.stats.deleted=Anzahl zu löschender Einträge +import.stats.faulty=Anzahl fehlerhafter Einträge (s. Statustooltip) +import.stats.modified=Anzahl zu ändernder Einträge +import.stats.new=Anzahl neuer Einträge import.stats.total=Gesamtanzahl -import.stats.unknown=Anzahl unbekannter Einträge -import.stats.unmodified=Anzahl unverändert Einträge +import.stats.unknown=Anzahl unbekannter Einträge +import.stats.unmodified=Anzahl unverändert Einträge import.title=Import-Tool -index.development=Webseite für Entwicklung +index.development=Webseite für Entwicklung index.website=Webseite index.welcome=Willkommen bei ProjectForge. -jira.chooseProject=--- JIRA-Projekt wählen --- -jobs.error.refusedByAnotherRunningJob=Job konnte nicht gestartet werden, weil bereits ein anderer Job für die gleiche Queue läuft. -jobs.error.waitingTimeExceeded=Job fehlgeschlagen (Zeitüberschreitung). Der Job konnte nicht gestartet werden, weil er von anderen Job(s) zu lange blockiert wurde. +jira.chooseProject=--- JIRA-Projekt wählen --- +jobs.error.refusedByAnotherRunningJob=Job konnte nicht gestartet werden, weil bereits ein anderer Job für die gleiche Queue läuft. +jobs.error.waitingTimeExceeded=Job fehlgeschlagen (Zeitüberschreitung). Der Job konnte nicht gestartet werden, weil er von anderen Job(s) zu lange blockiert wurde. jobs.import.action.reconcile=Abgleichen -jobs.import.action.reconcile.tooltip=Beim Abgleichen werden alle hochgeladenen Datensätze mit den in der Datenbank bereits vorhandenen Daten verglichen. +jobs.import.action.reconcile.tooltip=Beim Abgleichen werden alle hochgeladenen Datensätze mit den in der Datenbank bereits vorhandenen Daten verglichen. jobs.job.cancel.confirmationMessage=Soll der Job jetzt unwiderruflich abgebrochen werden? jobs.job.runtime=Laufzeit jobs.job.startedAt=Gestartet @@ -1574,7 +1574,7 @@ jobs.job.status.cancelled=Abgebrochen jobs.job.status.failed=Fehlerhaft jobs.job.status.finished=Fertig jobs.job.status.refused=Abgelehnt -jobs.job.status.running=Läuft +jobs.job.status.running=Läuft jobs.job.status.waiting=Wartend jobs.job.terminatedAt=Beendet jobs.job.title=Jobtitel @@ -1585,11 +1585,11 @@ label.exportFormat=Exportformat label.filterSettings=Filtereinstellungen label.help=Hilfe label.hint=Hinweis -label.historyOfChanges=Änderungshistorie +label.historyOfChanges=Änderungshistorie label.null= -label.onlyActiveEntries=Nur aktive Einträge +label.onlyActiveEntries=Nur aktive Einträge label.options=Optionen -label.pageSize=Seitengröße +label.pageSize=Seitengröße label.position.short=Pos label.result=Ergebnis label.resultset=Ergebnis @@ -1599,26 +1599,26 @@ label.sendEMailNotification=E-Mail-Benachrichtigung versenden? label.sendShortMessage=SMS an Bearbeiter versenden? ldap=LDAP ldap.gidNumber=GID number -ldap.gidNumber.alreadyInUse=Die GID-Nummer ist bereits an eine andere Gruppe vergeben. Die nächste freie GID-Nummer lautet: {0}. -ldap.gidNumber.createDefault.tooltip=Erzeugt eine neue GID (die nächste freie >= 1.000). Achtung: ProjectForge löscht keine Posix/Samba-Accounts im LDAP, die einmal angelegt worden sind! -ldap.gidNumber.tooltip=ProjectForges versucht die größte bereits durch ProjectForge vergebene GID-Nummer zu finden und schlägt die nächst höhere Nummer als nächste freie GID-Nummer vor. Dabei startet ProjectForge mit 1.000, um nicht mit GIDs der Betriebssysteme zu kollidieren (z. B. mit Systemgruppen). +ldap.gidNumber.alreadyInUse=Die GID-Nummer ist bereits an eine andere Gruppe vergeben. Die nächste freie GID-Nummer lautet: {0}. +ldap.gidNumber.createDefault.tooltip=Erzeugt eine neue GID (die nächste freie >= 1.000). Achtung: ProjectForge löscht keine Posix/Samba-Accounts im LDAP, die einmal angelegt worden sind! +ldap.gidNumber.tooltip=ProjectForges versucht die größte bereits durch ProjectForge vergebene GID-Nummer zu finden und schlägt die nächst höhere Nummer als nächste freie GID-Nummer vor. Dabei startet ProjectForge mit 1.000, um nicht mit GIDs der Betriebssysteme zu kollidieren (z. B. mit Systemgruppen). ldap.homeDirectory=Home directory ldap.loginShell=Login shell ldap.posixAccount=Posix account ldap.sambaAccount=Samba account ldap.sambaNTPassword=Samba-Passwort ldap.sambaNTPassword.subtitle=NT hashed Passwort -ldap.sambaNTPassword.tooltip=Das Samba-Passwort wird immer dann im LDAP gesetzt, wenn der Benutzer sein Password ändert. Falls es im LDAP nicht gesetzt wird, muss sich der Benutzer mindestens das nächste Mal an ProjectForge anmelden bevor ein etwaiger Angemeldet-Bleiben-Mechanismum greift. +ldap.sambaNTPassword.tooltip=Das Samba-Passwort wird immer dann im LDAP gesetzt, wenn der Benutzer sein Password ändert. Falls es im LDAP nicht gesetzt wird, muss sich der Benutzer mindestens das nächste Mal an ProjectForge anmelden bevor ein etwaiger Angemeldet-Bleiben-Mechanismum greift. ldap.sambaPrimaryGroupSID=Primary group SID. ldap.sambaPrimaryGroupSID.tooltip=Die Samba primary group SID ist optional. ldap.sambaSID=Samba SID -ldap.sambaSID.alreadyInUse=Die Samba-SID ist bereits an einen anderen Benutzer vergeben. Die nächste frei Samba-SID-Endziffer lautet: {0}. -ldap.sambaSID.createDefault.tooltip=Erzeugt eine neue Samba SID (die nächste freie >= 1.000). Achtung: ProjectForge löscht keine Posix/Samba-Accounts im LDAP, die einmal angelegt worden sind! +ldap.sambaSID.alreadyInUse=Die Samba-SID ist bereits an einen anderen Benutzer vergeben. Die nächste frei Samba-SID-Endziffer lautet: {0}. +ldap.sambaSID.createDefault.tooltip=Erzeugt eine neue Samba SID (die nächste freie >= 1.000). Achtung: ProjectForge löscht keine Posix/Samba-Accounts im LDAP, die einmal angelegt worden sind! ldap.sambaSID.tooltip=Die Samba-SID ist ein Wert, der aus dem in projectforge.properties konfiguriertem Samba-SID-Prefix und der UID number gebildet wird. ldap.uidNumber=UID number -ldap.uidNumber.alreadyInUse=Die UID-Nummer ist bereits an eine:n andere:n Benutzer:in vergeben. Die nächste freie UID-Nummer lautet: {0}. -ldap.uidNumber.createDefault.tooltip=Erzeugt eine neue UID (die nächste freie >= 1.000), setzt die Standard-GID (konfiguriert in projectforge.properties), das Homedirectory und die konfigurierte Standard-Login-Shell. Achtung: ProjectForge löscht keine Posix/Samba-Accounts im LDAP, die einmal angelegt worden sind! -ldap.uidNumber.tooltip=ProjectForge versucht die größte bereits durch ProjectForge vergebene UID-Nummer zu finden und schlägt die nächst höhere Nummer als nächste freie UID-Nummer vor. Dabei startet ProjectForge mit 1.000, um nicht mit UIDs der Betriebssysteme zu kollidieren (z. B. mit Systembenutzern). +ldap.uidNumber.alreadyInUse=Die UID-Nummer ist bereits an eine:n andere:n Benutzer:in vergeben. Die nächste freie UID-Nummer lautet: {0}. +ldap.uidNumber.createDefault.tooltip=Erzeugt eine neue UID (die nächste freie >= 1.000), setzt die Standard-GID (konfiguriert in projectforge.properties), das Homedirectory und die konfigurierte Standard-Login-Shell. Achtung: ProjectForge löscht keine Posix/Samba-Accounts im LDAP, die einmal angelegt worden sind! +ldap.uidNumber.tooltip=ProjectForge versucht die größte bereits durch ProjectForge vergebene UID-Nummer zu finden und schlägt die nächst höhere Nummer als nächste freie UID-Nummer vor. Dabei startet ProjectForge mit 1.000, um nicht mit UIDs der Betriebssysteme zu kollidieren (z. B. mit Systembenutzern). ldap.user.gidNumber.tooltip=Der Standardwert kann in projectforge.properties konfiguriert werden: projectforge.ldap.posixAccountsDefaultGidNumber ldap.wlanSambaPassword=WLAN/Samba Passwort legalAffaires.contract.coContractorA=Vertragspartner:in A @@ -1626,7 +1626,7 @@ legalAffaires.contract.coContractorB=Vertragspartner:in B legalAffaires.contract.contractPersonA=Ansprechpartner:in A legalAffaires.contract.contractPersonB=Ansprechpartner:in B legalAffaires.contract.error.numberAlreadyExists=Die Vertragsnummer ist bereits vergeben. -legalAffaires.contract.error.numberNotConsecutivelyNumbered=Die Vertragsnummer ist nicht die nächstfolgende (nicht fortlaufend). +legalAffaires.contract.error.numberNotConsecutivelyNumbered=Die Vertragsnummer ist nicht die nächstfolgende (nicht fortlaufend). legalAffaires.contract.number=Vertragsnummer legalAffaires.contract.signerA=Unterzeichner:in A legalAffaires.contract.signerB=Unterzeichner:in B @@ -1641,8 +1641,8 @@ legalAffaires.contract.status.suspended=verworfen legalAffaires.contract.status.unknown=unbekannt legalAffaires.contract.title.add=Neuer Vertrag legalAffaires.contract.title.edit=Vertrag bearbeiten -legalAffaires.contract.title.heading=Verträge -legalAffaires.contract.title.list=Verträge +legalAffaires.contract.title.heading=Verträge +legalAffaires.contract.title.list=Verträge legalAffaires.contract.type=Typ legalAffaires.contract.validity=Laufzeit legalAffaires.contract.validity.from=Laufzeit von @@ -1658,31 +1658,31 @@ locale.zh=Chinesisch ### not translated: log.level.warn=Warn login.adminLoginRequired=Wartungsmodus: Bitte als Administrator:in anmelden! login.error.loginExpired=Die Anmeldung war nicht erfolgreich. Bitte Administrator:in kontaktieren, da der Zugang abgelaufen ist. -login.error.loginFailed=Die Anmeldung war nicht erfolgreich. Bitte Eingabe nochmals prüfen (Groß-/Kleinschreibung bitte beachten). +login.error.loginFailed=Die Anmeldung war nicht erfolgreich. Bitte Eingabe nochmals prüfen (Groß-/Kleinschreibung bitte beachten). login.lastLogin=Letze Anmeldung login.loginFailures=Anmeldefehlversuche -login.passwordReset=Passwort zurücksetzen -login.passwordReset.info=Ein Link zum Passwortrücksetzen wird per E-Mail gesendet (ein 2. Faktor wird benötigt). +login.passwordReset=Passwort zurücksetzen +login.passwordReset.info=Ein Link zum Passwortrücksetzen wird per E-Mail gesendet (ein 2. Faktor wird benötigt). login.stayLoggedIn=An diesem Browser angemeldet bleiben. login.stayLoggedIn.invalidateAllStayLoggedInSessions=Alle Angemeldet-bleiben-Sitzungen beenden. login.stayLoggedIn.invalidateAllStayLoggedInSessions.successfullDeleted=Alle Angemeldet-bleiben-Sitzungen wurden erfolgreich beendet. -login.stayLoggedIn.invalidateAllStayLoggedInSessions.tooltip=Ãœber diese wird ein neuer Code generiert. Damit werden alle eventuell noch vorhanden Angemeldet-bleiben-Sitzungen ungültig. -login.stayLoggedIn.tooltip=Du kannst an diesem Browser angemeldet bleiben, wenn du diese Funktion anwählst. Bei Abmeldung wird dieses Merkmal (Cookie) wieder entfernt. Änderst du dein Passwort und/oder Benutzer:innamen werden alle Browseranmeldungen zunächst ungültig. Außerdem kannst di unter Mein Account alle vorhandenen Browseranmeldungen auf ungültig setzen. Diese Funktion kann immer wieder erneut genutzt werden. -login.successful=Anmeldung erfolgreich. Viel Spaß mit ProjectForge! -login.timeOffset=Der Zugang ist noch für {0} Sekunden gesperrt auf Grund von {1} Fehlanmeldungen. Bitte später nochmals versuchen. +login.stayLoggedIn.invalidateAllStayLoggedInSessions.tooltip=Ãœber diese wird ein neuer Code generiert. Damit werden alle eventuell noch vorhanden Angemeldet-bleiben-Sitzungen ungültig. +login.stayLoggedIn.tooltip=Du kannst an diesem Browser angemeldet bleiben, wenn du diese Funktion anwählst. Bei Abmeldung wird dieses Merkmal (Cookie) wieder entfernt. Änderst du dein Passwort und/oder Benutzer:innamen werden alle Browseranmeldungen zunächst ungültig. Außerdem kannst di unter Mein Account alle vorhandenen Browseranmeldungen auf ungültig setzen. Diese Funktion kann immer wieder erneut genutzt werden. +login.successful=Anmeldung erfolgreich. Viel Spaß mit ProjectForge! +login.timeOffset=Der Zugang ist noch für {0} Sekunden gesperrt auf Grund von {1} Fehlanmeldungen. Bitte später nochmals versuchen. login.title=Anmeldung logout.successful=Erfolgreich abgemeldet. mail.error.exception=Beim E-Mailversand trat ein Fehler auf, der protokolliert wurde. Bitte eine:n Systemadministrator:in kontaktieren. -mail.error.missingToAddress=Der E-Mailversand wurde abgebrochen, da keine Empfänger:inadresse angegeben ist. -mail.template.closing=Viel Spaß mit ProjectForge\! +mail.error.missingToAddress=Der E-Mailversand wurde abgebrochen, da keine Empfänger:inadresse angegeben ist. +mail.template.closing=Viel Spaß mit ProjectForge\! mail.template.opening=Hallo {0}, -massUpdate.changeSelection=Auswähl ändern -massUpdate.confirmQuestion=Sollen nun alle {0} Objekte geändert werden? -massUpdate.entriesFound=Es sind {0} Einträge ausgewählt. -massUpdate.error.invalidOptionMix=Sich widersprechende Aktionen gewählt. -massUpdate.error.maximumNumberOfAllowedMassUpdatesExceeded=Die maximal zulässige Anzahl {0} von zu ändernden Elementen für eine Massenänderung wurde überschritten. Aus Qualitätsgründen wird diese Operation daher nicht zugelassen. -massUpdate.error.noEntriesSelected=Keine ausgewählten Datensätze gefunden. Bitte vorher eine Auswahl treffen. -massUpdate.error.nothingToDo=Es gibt nichts zu tun. Bitte Felder zum Ändern auswählen. +massUpdate.changeSelection=Auswähl ändern +massUpdate.confirmQuestion=Sollen nun alle {0} Objekte geändert werden? +massUpdate.entriesFound=Es sind {0} Einträge ausgewählt. +massUpdate.error.invalidOptionMix=Sich widersprechende Aktionen gewählt. +massUpdate.error.maximumNumberOfAllowedMassUpdatesExceeded=Die maximal zulässige Anzahl {0} von zu ändernden Elementen für eine Massenänderung wurde überschritten. Aus Qualitätsgründen wird diese Operation daher nicht zugelassen. +massUpdate.error.noEntriesSelected=Keine ausgewählten Datensätze gefunden. Bitte vorher eine Auswahl treffen. +massUpdate.error.nothingToDo=Es gibt nichts zu tun. Bitte Felder zum Ändern auswählen. massUpdate.error.table.element=Element massUpdate.error.table.message=Meldung massUpdate.error.table.title=Fehlermeldungen @@ -1691,52 +1691,52 @@ massUpdate.error.unspecifiedError=Es ist ein Fehler aufgetreten. massUpdate.excel.column.element=Element massUpdate.excel.column.new=neu massUpdate.excel.column.old=alt -massUpdate.excel.download=Exceldatei mit allen Änderungen -massUpdate.field.checkbox4appending=Anhängen? -massUpdate.field.checkbox4appending.info=Wenn ausgewählt, wird der Text angehängt (wenn nicht bereits im Feld enthalten). Wenn nicht ausgewählt, wird das bestehende Feld ersetzt. -massUpdate.field.checkbox4deletion=Löschen? -massUpdate.field.checkbox4deletion.info=Wenn diese Option ausgewählt ist, wird für alle Datensätze dieses Feld gelöscht (bzw. geleert). Wenn ein Text angegeben ist, wird nur der Text gelöscht, wenn vorhanden. +massUpdate.excel.download=Exceldatei mit allen Änderungen +massUpdate.field.checkbox4appending=Anhängen? +massUpdate.field.checkbox4appending.info=Wenn ausgewählt, wird der Text angehängt (wenn nicht bereits im Feld enthalten). Wenn nicht ausgewählt, wird das bestehende Feld ersetzt. +massUpdate.field.checkbox4deletion=Löschen? +massUpdate.field.checkbox4deletion.info=Wenn diese Option ausgewählt ist, wird für alle Datensätze dieses Feld gelöscht (bzw. geleert). Wenn ein Text angegeben ist, wird nur der Text gelöscht, wenn vorhanden. massUpdate.field.replace=Ersetzen durch? massUpdate.field.replace.info=Wenn hier ein Text angegeben ist, werden alle gefundenen Vorkommnisse durch diesen Text ersetzt. -massUpdate.info=Im Protokoll finden sich detaillierte Informationen zur Massenänderung. -massUpdate.result={0} Einträge wurden bearbeitet: {1} geändert, {2} unverändert und {3} fehlerhaft. -massUpdate.result.excel.title=Massenänderungen +massUpdate.info=Im Protokoll finden sich detaillierte Informationen zur Massenänderung. +massUpdate.result={0} Einträge wurden bearbeitet: {1} geändert, {2} unverändert und {3} fehlerhaft. +massUpdate.result.excel.title=Massenänderungen menu.2FASetup=2. Faktor einrichten menu.accessList=Zugriffsverwaltung menu.addNewEntry=Neuer Eintrag -menu.addressbookList=Adressbücher +menu.addressbookList=Adressbücher menu.addressList=Adressen menu.addressListClassics=Adressen klassisch menu.administration=Administration menu.adminLogbuch=Admin-Logbuch menu.birthdayButler=Geburtstagsbutler menu.birthdays=Geburtstage -menu.bookList=Bücher +menu.bookList=Bücher menu.calendar=Kalender -menu.changePassword=Passwort ändern -menu.changeWlanPassword=WLAN/Samba Passwort ändern +menu.changePassword=Passwort ändern +menu.changeWlanPassword=WLAN/Samba Passwort ändern menu.common=Allgemein menu.configuration=Konfiguration -menu.configureMenu.tooltip=Per Drag & Drop kann das persönliche Menü gestaltet werden. +menu.configureMenu.tooltip=Per Drag & Drop kann das persönliche Menü gestaltet werden. menu.contactList=Kontakte -menu.contracts=Verträge -menu.customize.completeMenu=Komplettes Menü -menu.customize.create=Neuer Menüordner +menu.contracts=Verträge +menu.customize.completeMenu=Komplettes Menü +menu.customize.create=Neuer Menüordner menu.customize.enterNewName=Neuen Namen eingeben -menu.customize.help=Per Drag&Drop können die Menüeinträge umsortiert werden. Bitte beachten, dass nur Menueinträge bis zur 3. Einrückebene genutzt werden. Menüeinträge aus dem rechten, kompletten Menü können per Drag&Drop in das persönliche Menü übernommen werden. Zum Löschen, Anlegen und Umbennen bitte das Kontextmenü verwenden (rechte Maustaste). +menu.customize.help=Per Drag&Drop können die Menüeinträge umsortiert werden. Bitte beachten, dass nur Menueinträge bis zur 3. Einrückebene genutzt werden. Menüeinträge aus dem rechten, kompletten Menü können per Drag&Drop in das persönliche Menü übernommen werden. Zum Löschen, Anlegen und Umbennen bitte das Kontextmenü verwenden (rechte Maustaste). menu.customize.remove=Entfernen menu.customize.rename=Umbenennen -menu.customize.title=Persönliches Menü +menu.customize.title=Persönliches Menü menu.documentation=Dokumentation ### not translated: menu.dvelop=D-velop menu.faq=FAQ -menu.favorite.maxSizeExceeded=Das Menü ist leider zu groß geworden und kann daher nicht gespeichert werden. +menu.favorite.maxSizeExceeded=Das Menü ist leider zu groß geworden und kann daher nicht gespeichert werden. menu.fibu=Fibu -menu.fibu.buchungssaetze=Buchungssätze +menu.fibu.buchungssaetze=Buchungssätze menu.fibu.datevImport=Datev-Import menu.fibu.eingangsrechnungen=Kreditorenrechnungen menu.fibu.employees=Mitarbeitende -menu.fibu.employeeSalaries=Gehälter +menu.fibu.employeeSalaries=Gehälter menu.fibu.konten=Buchungskonten menu.fibu.kost=Kost menu.fibu.kost1=Kost1 @@ -1744,12 +1744,12 @@ menu.fibu.kost2=Kost2 menu.fibu.kost2arten=Kost2-Arten menu.fibu.kunden=Kunden menu.fibu.orderbook=Auftragsbuch -menu.fibu.orderbook.htmlSuffixTooltip=Abgeschlossene und noch nicht vollständig fakturierte Aufträge. +menu.fibu.orderbook.htmlSuffixTooltip=Abgeschlossene und noch nicht vollständig fakturierte Aufträge. menu.fibu.projekte=Projekte menu.fibu.rechnungen=Debitorenrechnungen menu.fibu.reporting.reportObjectives=Report-Objectives menu.gantt=Gantt -menu.gear.customizeMenu=Menü anpassen +menu.gear.customizeMenu=Menü anpassen menu.gear.feedback=Feedback senden menu.gear.layoutsettings=Layouteinstellungen menu.gear.showBookmark=Seite als Link @@ -1762,9 +1762,9 @@ menu.imageCropper=ImageCropper menu.login=Anmelden menu.logout=Abmelden ### not translated: menu.luceneConsole=Lucene Console -menu.main.tip1=Häufig verwendete Menüeinträge können per Drag&Drop direkt in das persönliche Menü nach oben kopiert werden. Sie sind dann mit einem Mausklick direkt erreichbar. -menu.main.tip2=Die Menüeinträge können auch in die Lesezeichenleiste per Drag&Drop eingefügt werden. Abhängig vom Browser und Betriebssystem können die Menüeinträge dann auch mit Tastenkürzel aufgerufen werden (z. B. CMD-Nummer unter Apple-Safari). -menu.main.title=Hauptmenü +menu.main.tip1=Häufig verwendete Menüeinträge können per Drag&Drop direkt in das persönliche Menü nach oben kopiert werden. Sie sind dann mit einem Mausklick direkt erreichbar. +menu.main.tip2=Die Menüeinträge können auch in die Lesezeichenleiste per Drag&Drop eingefügt werden. Abhängig vom Browser und Betriebssystem können die Menüeinträge dann auch mit Tastenkürzel aufgerufen werden (z. B. CMD-Nummer unter Apple-Safari). +menu.main.title=Hauptmenü menu.misc=Verschiedenes menu.monthlyEmployeeReport=Monatsbericht menu.monthlyEmployeeReport.fileprefix=Monatsbericht @@ -1784,14 +1784,14 @@ menu.plugins.teamcal=Kalenderliste menu.poll=Umfragen menu.projectmanagement=Projektmanagement menu.reindexAllDatabaseEntries=Suchindex voll indizieren -menu.reindexAllDatabaseEntries.tooltip.content=Der Suchindex wird komplett neu aufgebaut. Bei vielen Einträgen (z. B. 100.000) kann das einige Minuten dauern und die Systemperformance beinträchtigt werden. Deshalb steht diese Funktion nur Administratoren zur Verfügung. +menu.reindexAllDatabaseEntries.tooltip.content=Der Suchindex wird komplett neu aufgebaut. Bei vielen Einträgen (z. B. 100.000) kann das einige Minuten dauern und die Systemperformance beinträchtigt werden. Deshalb steht diese Funktion nur Administratoren zur Verfügung. menu.reindexAllDatabaseEntries.tooltip.title=Suchindex voll indizieren menu.reindexNewestDatabaseEntries=Suchindex reindizieren -menu.reindexNewestDatabaseEntries.tooltip.content=Es werden alle Einträge, die seit gestern angelegt oder modifiziert wurden, maximal aber 1.000 Einträge, re-indiziert (Dauer weniger als 1 Sekunde). +menu.reindexNewestDatabaseEntries.tooltip.content=Es werden alle Einträge, die seit gestern angelegt oder modifiziert wurden, maximal aber 1.000 Einträge, re-indiziert (Dauer weniger als 1 Sekunde). menu.reindexNewestDatabaseEntries.tooltip.title=Suchindex reindizieren menu.reporting=Reporting -menu.resetFilter=Filter zurücksetzen -menu.resetFilter.info=Setzt den aktuellen Suchfilter zurück, wenn Probleme beim Suchen bestehen. +menu.resetFilter=Filter zurücksetzen +menu.resetFilter.info=Setzt den aktuellen Suchfilter zurück, wenn Probleme beim Suchen bestehen. menu.scriptList=Scriptliste menu.search=Suche menu.sendSms=SMS senden @@ -1802,18 +1802,18 @@ menu.taskTree=Strukturbaum menu.testReports=Testreports menu.timesheetList=Zeitberichte menu.userList=Benutzer:innen -menu.vacation=Urlaubseinträge +menu.vacation=Urlaubseinträge menu.vacation.lastyear=Urlaub aus dem Vorjahr menu.vacation.leaveaccount=Urlaubskonto menu.vacation.leaveAccountEntry=Urlaubskontokorrekturen message.cancelAction=Aktion ''{0}'' wurde abgebrochen. -message.successfullChanged=Änderung erfolgreich durchgeführt. -message.successfullCompleted=Aktion erfolgreich durchgeführt\: {0} +message.successfullChanged=Änderung erfolgreich durchgeführt. +message.successfullCompleted=Aktion erfolgreich durchgeführt\: {0} message.title=Meldung -message.wicket.pageExpired=Die aufgerufene Seite ist nicht mehr verfügbar. +message.wicket.pageExpired=Die aufgerufene Seite ist nicht mehr verfügbar. multiselection.aggrid.selection.info.message=\ -* Gemeinsam mit der Hochstelltaste bzw. Steuerungstaste können mehrere Zeilen per Mausklick gleichzeitig ausgewählt werden.\n\ -* Mit den Pfeiltasten kann in den Zeilen navigiert werden und über die Leertaste einzelne Zeilen selektiert und deselektiert werden. +* Gemeinsam mit der Hochstelltaste bzw. Steuerungstaste können mehrere Zeilen per Mausklick gleichzeitig ausgewählt werden.\n\ +* Mit den Pfeiltasten kann in den Zeilen navigiert werden und über die Leertaste einzelne Zeilen selektiert und deselektiert werden. multiselection.aggrid.selection.info.title=Mehrfachauswahl multiselection.button=Mehrfachauswahl orga.post.inhalt=Inhalt @@ -1821,12 +1821,12 @@ orga.post.type=Art orga.post.type.brief=Brief orga.post.type.e-mail=E-Mail orga.post.type.einschreiben=Einschreiben -orga.post.type.einschreibenMitRueckschein=Einschreiben mit Rückschein +orga.post.type.einschreibenMitRueckschein=Einschreiben mit Rückschein orga.post.type.fax=Fax orga.post.type.lieferung=Lieferung -orga.post.type.paeckchen=Päckchen +orga.post.type.paeckchen=Päckchen orga.post.type.paket=Paket -orga.postausgang.empfaenger=Empfänger:in +orga.postausgang.empfaenger=Empfänger:in orga.postausgang.person=z. Hd. orga.postausgang.title.add=Postausgang anlegen orga.postausgang.title.edit=Postausgang bearbeiten @@ -1848,7 +1848,7 @@ orga.visitorbook.number=Nummer orga.visitorbook.timeofvisit=Besuchszeit orga.visitorbook.timeofvisit.arrive=Ankunftszeit (HH:MM) orga.visitorbook.timeofvisit.depart=Abreisezeit (HH:MM) -orga.visitorbook.title.add=Besuch hinzufügen +orga.visitorbook.title.add=Besuch hinzufügen orga.visitorbook.title.edit=Besuch anpassen orga.visitorbook.title.heading=Besuchsbuch orga.visitorbook.title.list=Besuchsbuch Ãœbersicht @@ -1864,18 +1864,18 @@ panel.error.groupNotFound=Gruppe nicht existent. panel.error.projectNotFound=Projekt nicht existent. password.forgotten.link=Zugangsdaten vergessen? password.forgotten.mail.subject=Passwortreset ProjectForge® -password.forgotten.mailSentTo=E-Mail mit dem Passwortrücksetzen-Link wurde gesendet an ''{0}''. Bitte im Spam-Ordner schauen bzw. überprüfen, ob der Benutzer:innenname bzw. E-Mail korrekt ist. -password.forgotten.request=Rücksetzen anfordern +password.forgotten.mailSentTo=E-Mail mit dem Passwortrücksetzen-Link wurde gesendet an ''{0}''. Bitte im Spam-Ordner schauen bzw. überprüfen, ob der Benutzer:innenname bzw. E-Mail korrekt ist. +password.forgotten.request=Rücksetzen anfordern password.forgotten.title=Passwort vergessen -password.reset.error=Es wurde keine gültige Passwortreset-Sitzung gefunden. Entweder ist sie schon verbraucht, abgelaufen oder nicht vorhanden. Bitte erneut versuchen. -password.reset.mail.link=Ãœber diesen Link kann das Passwort zurückgesetzt werden: {0} -password.reset.mail.message.1=Bitte beachten: Es wird für das Zurücksetzen aus Sicherheitsgründen ein zweiter Faktor benötigt. -password.reset.mail.message.2=Wenn noch kein zweiter, persönlicher Faktor konfiguriert wurde (Authenticator-App oder Mobilfunknummer), muss ein ProjectForge-Administrator kontaktiert werden. -password.reset.title=Passwort zurücksetzen +password.reset.error=Es wurde keine gültige Passwortreset-Sitzung gefunden. Entweder ist sie schon verbraucht, abgelaufen oder nicht vorhanden. Bitte erneut versuchen. +password.reset.mail.link=Ãœber diesen Link kann das Passwort zurückgesetzt werden: {0} +password.reset.mail.message.1=Bitte beachten: Es wird für das Zurücksetzen aus Sicherheitsgründen ein zweiter Faktor benötigt. +password.reset.mail.message.2=Wenn noch kein zweiter, persönlicher Faktor konfiguriert wurde (Authenticator-App oder Mobilfunknummer), muss ein ProjectForge-Administrator kontaktiert werden. +password.reset.title=Passwort zurücksetzen password.reset.username_email=Benutzer:inname/E-Mail personal.statistics.timesheetDisciplineChart.title=Zeitnahes Erfassen von Zeitberichten ist ein wichtiger Grundpfeiler erfolgreichen Projektmanagements! -personal.statistics.timesheetDisciplineChart1.legend=Insgesamt sind {1} Arbeitszeitstunden in den letzten {0} Tagen angefallen. Bisher wurden für diesen Zeitraum {2} Stunden erfasst. -personal.statistics.timesheetDisciplineChart2.legend=In den letzten {0} Tagen wurden Zeitberichte nach durchschnittlich {2} Tagen erfasst. Die Zielgröße ist maximal {1} Tage. +personal.statistics.timesheetDisciplineChart1.legend=Insgesamt sind {1} Arbeitszeitstunden in den letzten {0} Tagen angefallen. Bisher wurden für diesen Zeitraum {2} Stunden erfasst. +personal.statistics.timesheetDisciplineChart2.legend=In den letzten {0} Tagen wurden Zeitberichte nach durchschnittlich {2} Tagen erfasst. Die Zielgröße ist maximal {1} Tage. personal.statistics.title=Meine Statistiken plugins.teamcal.access=Zugriffsrecht plugins.teamcal.access.groups=Gruppen @@ -1885,9 +1885,9 @@ plugins.teamcal.adminAccess=Adminrecht plugins.teamcal.attendee.email.accept=Diesen Termin akzeptieren. plugins.teamcal.attendee.email.content.deleted={0} hat den Termin "{1}" abgesagt. plugins.teamcal.attendee.email.content.new={0} hat Sie zu "{1}" eingeladen. -plugins.teamcal.attendee.email.content.updated={0} hat den Termin "{1}" geändert. +plugins.teamcal.attendee.email.content.updated={0} hat den Termin "{1}" geändert. plugins.teamcal.attendee.email.decline=Diesen Termin ablehnen. -plugins.teamcal.attendee.email.invalid=Mindestens eine Teilnehmer:in E-Mailadresse ist ungültig +plugins.teamcal.attendee.email.invalid=Mindestens eine Teilnehmer:in E-Mailadresse ist ungültig plugins.teamcal.attendee.email.subject.deleted="{1}" wurde abgesagt plugins.teamcal.attendee.email.subject.new={0} hat Sie zu "{1}" eingeladen plugins.teamcal.attendee.email.subject.updated="{1}" wurde aktuallisiert @@ -1904,32 +1904,32 @@ plugins.teamcal.attendee.status.new=neu plugins.teamcal.attendee.status.tentative=unverbindlich plugins.teamcal.attendees=Teilnehmer:innen plugins.teamcal.calendar=Kalender -plugins.teamcal.calendar.filter.choose=Kalenderfilter auswählen oder neue Filter anlegen. -plugins.teamcal.calendar.filter.edit=Angezeigten Kalenderfilter ändern. +plugins.teamcal.calendar.filter.choose=Kalenderfilter auswählen oder neue Filter anlegen. +plugins.teamcal.calendar.filter.edit=Angezeigten Kalenderfilter ändern. plugins.teamcal.calendar.filter.newEntry=--- Neuer Kalenderfilter --- -plugins.teamcal.calendar.filterDialog.closeButton.tooltip=Zum Ãœbernehmen der Filteränderungen können Sie irgendwo außerhalb dieses Dialogs klicken. Wenn Sie die Änderungen verwerfen möchten, so drücken Sie bitte den Abbrechen-Knopf. +plugins.teamcal.calendar.filterDialog.closeButton.tooltip=Zum Ãœbernehmen der Filteränderungen können Sie irgendwo außerhalb dieses Dialogs klicken. Wenn Sie die Änderungen verwerfen möchten, so drücken Sie bitte den Abbrechen-Knopf. plugins.teamcal.calendar.filterDialog.newTemplateName=Neuer Filter plugins.teamcal.calendar.filterDialog.title=Kalenderfilter -plugins.teamcal.calendar.listAndIcsExport.tooltip=Liste der Kalender inklusive Download und Abonnement im ICS-Format. Hier können auch neue Kalender angelegt werden. +plugins.teamcal.calendar.listAndIcsExport.tooltip=Liste der Kalender inklusive Download und Abonnement im ICS-Format. Hier können auch neue Kalender angelegt werden. plugins.teamcal.calendar.refresh.tooltip=Kalender neu laden und extern abonnierte Kalender, sofern angezeigt, aktualisieren. plugins.teamcal.defaultCalendar=Standardkalender -plugins.teamcal.defaultCalendar.tooltip=Der Standardkalender ist der voreingestellte Kalender für neue Einträge. +plugins.teamcal.defaultCalendar.tooltip=Der Standardkalender ist der voreingestellte Kalender für neue Einträge. plugins.teamcal.description=Beschreibung -plugins.teamcal.download=Download Url für Kalender ''${calendar}'' -plugins.teamcal.dropIcsPanel.tooltip=Hier können ICS-Dateien per Drag&Drop hin gezogen werden, um Kalendereinträge zu importieren bzw. zu aktualisieren. +plugins.teamcal.download=Download Url für Kalender ''${calendar}'' +plugins.teamcal.dropIcsPanel.tooltip=Hier können ICS-Dateien per Drag&Drop hin gezogen werden, um Kalendereinträge zu importieren bzw. zu aktualisieren. plugins.teamcal.event.accept=Annehmen -plugins.teamcal.event.addNewAttendee=Teilnehmer:in hinzufügen ... +plugins.teamcal.event.addNewAttendee=Teilnehmer:in hinzufügen ... plugins.teamcal.event.allDay=Ganztags plugins.teamcal.event.andyou=und Sie plugins.teamcal.event.beginDate=Beginn -plugins.teamcal.event.changed=Termin geändert -plugins.teamcal.event.changedby=Geändert von +plugins.teamcal.event.changed=Termin geändert +plugins.teamcal.event.changedby=Geändert von plugins.teamcal.event.convert2Timesheet=Umwandeln in Zeitbericht plugins.teamcal.event.decline=Ablehnen -plugins.teamcal.event.deletedby=Gelöscht von +plugins.teamcal.event.deletedby=Gelöscht von plugins.teamcal.event.duplicatedUidFromDifferentUser=Es existiert bereits ein Ereignis mit der UID {0} im Kalendar {1} von einem oder einer anderen Benutzer:in. plugins.teamcal.event.duration=Dauer -plugins.teamcal.event.duration.error=Bitte überprüfen Sie das Start- und Enddatum. +plugins.teamcal.event.duration.error=Bitte überprüfen Sie das Start- und Enddatum. plugins.teamcal.event.endDate=Ende plugins.teamcal.event.event=Termin plugins.teamcal.event.event.allDay=Alle {0} Tage @@ -1950,22 +1950,22 @@ plugins.teamcal.event.expertSettings=Experteneinstellungen plugins.teamcal.event.invitation1=hat Sie zu plugins.teamcal.event.invitation2=eingeladen. plugins.teamcal.event.location=Ort -plugins.teamcal.event.location.changed=Ort geändert +plugins.teamcal.event.location.changed=Ort geändert plugins.teamcal.event.note=Notiz -plugins.teamcal.event.ownership=Eigentümer -plugins.teamcal.event.ownership.tooltip=Ändere diesen Wert nur, wenn du genau weißt, was du tust. Angehakt = ProjectForge ist Eigentümer, das Ereignis kann editiert werden und e-Mails an Teilnehmer werden versandt. +plugins.teamcal.event.ownership=Eigentümer +plugins.teamcal.event.ownership.tooltip=Ändere diesen Wert nur, wenn du genau weißt, was du tust. Angehakt = ProjectForge ist Eigentümer, das Ereignis kann editiert werden und e-Mails an Teilnehmer werden versandt. plugins.teamcal.event.recurrence=Wiederholung plugins.teamcal.event.recurrence.atthe=Am plugins.teamcal.event.recurrence.change.all=Alle Ereignisse -plugins.teamcal.event.recurrence.change.content=Möchten Sie alle Ereignisse, nur zukünftige oder nur dieses einzelne Ereignis ändern? -plugins.teamcal.event.recurrence.change.future=Alle zukünftigen Ereignisse +plugins.teamcal.event.recurrence.change.content=Möchten Sie alle Ereignisse, nur zukünftige oder nur dieses einzelne Ereignis ändern? +plugins.teamcal.event.recurrence.change.future=Alle zukünftigen Ereignisse plugins.teamcal.event.recurrence.change.single=Nur dieses Ereignis -plugins.teamcal.event.recurrence.change.text=Alle Änderungen betreffen: +plugins.teamcal.event.recurrence.change.text=Alle Änderungen betreffen: plugins.teamcal.event.recurrence.change.text.all=alle Ereignisse -plugins.teamcal.event.recurrence.change.text.allFuture=alle zukünfigen Ereignisse +plugins.teamcal.event.recurrence.change.text.allFuture=alle zukünfigen Ereignisse plugins.teamcal.event.recurrence.change.text.onlyCurrent=nur das aktuelle Ereignis -plugins.teamcal.event.recurrence.change.title=Sie ändern ein wiederkehrendes Ereignis. -plugins.teamcal.event.recurrence.changed=Wiederholung geändert +plugins.teamcal.event.recurrence.change.title=Sie ändern ein wiederkehrendes Ereignis. +plugins.teamcal.event.recurrence.changed=Wiederholung geändert plugins.teamcal.event.recurrence.customized=angepasst plugins.teamcal.event.recurrence.customized.all=Alle plugins.teamcal.event.recurrence.customized.day=Tag(e) @@ -1975,8 +1975,8 @@ plugins.teamcal.event.recurrence.customized.year=Jahr(e) plugins.teamcal.event.recurrence.day=Tag plugins.teamcal.event.recurrence.each=Jeden plugins.teamcal.event.recurrence.exDate=Ausnahmetage -plugins.teamcal.event.recurrence.exDate.tooltip=An diesen Tagen fällt der Wiederholungstermin aus. Bitte diesen Wert nicht manuell ändern, da er in einem besonderen Format vorliegen muss. -plugins.teamcal.event.recurrence.fifth=Fünfter +plugins.teamcal.event.recurrence.exDate.tooltip=An diesen Tagen fällt der Wiederholungstermin aus. Bitte diesen Wert nicht manuell ändern, da er in einem besonderen Format vorliegen muss. +plugins.teamcal.event.recurrence.fifth=Fünfter plugins.teamcal.event.recurrence.first=Erster plugins.teamcal.event.recurrence.fourth=Vierter plugins.teamcal.event.recurrence.friday=Freitag @@ -2007,7 +2007,7 @@ plugins.teamcal.event.reminder.MINUTES_BEFORE=Minuten davor plugins.teamcal.event.reminder.NONE=Ohne plugins.teamcal.event.reminder.options=Erinnerungsoptionen plugins.teamcal.event.reminder.title=Erinnerung -plugins.teamcal.event.reminder.tooltip=Die Erinnerung sollte von Endgeräten durchgeführt werden (Apple iCal, iPhone, Android-Telefon, Google-Calendar oder MS Outlook). ProjectForge führt selbst keine Erinnerung durch. +plugins.teamcal.event.reminder.tooltip=Die Erinnerung sollte von Endgeräten durchgeführt werden (Apple iCal, iPhone, Android-Telefon, Google-Calendar oder MS Outlook). ProjectForge führt selbst keine Erinnerung durch. plugins.teamcal.event.showreplies=Antworten anzeigen ... plugins.teamcal.event.status.committed=zugesagt plugins.teamcal.event.status.declined=abgelehnt @@ -2016,7 +2016,7 @@ plugins.teamcal.event.status.unknown=unbekannt plugins.teamcal.event.subject=Betreff plugins.teamcal.event.teamCal=Teamkalender plugins.teamcal.event.tentative=Vielleicht -plugins.teamcal.event.title.add=Ereignis hinzufügen +plugins.teamcal.event.title.add=Ereignis hinzufügen plugins.teamcal.event.title.edit=Ereignis bearbeiten plugins.teamcal.event.title.heading=Ereignis plugins.teamcal.event.title.list=Ereignisliste @@ -2024,12 +2024,12 @@ plugins.teamcal.event.uid=UID plugins.teamcal.event.update=wurde aktualisiert. plugins.teamcal.events=Ereignisse plugins.teamcal.export.holidays=Feiertage exportieren -plugins.teamcal.export.holidays.tooltip=Download oder Abonnement von Feiertagen aus ProjectForge. Die Sprache kann unter 'Mein Zugang' geändert werden. +plugins.teamcal.export.holidays.tooltip=Download oder Abonnement von Feiertagen aus ProjectForge. Die Sprache kann unter 'Mein Zugang' geändert werden. plugins.teamcal.export.reminder.checkbox=Erinnerungen exportieren -plugins.teamcal.export.reminder.checkbox.tooltip=Wenn ausgewählt, werden auch die Erinnerungsfelder der Ereignisse exportiert, andernfalls werden sie ignoriert. +plugins.teamcal.export.reminder.checkbox.tooltip=Wenn ausgewählt, werden auch die Erinnerungsfelder der Ereignisse exportiert, andernfalls werden sie ignoriert. plugins.teamcal.export.timesheets=Zeitberichte exportieren plugins.teamcal.export.weekOfYears=Kalenderwochen exportieren -plugins.teamcal.export.weekOfYears.tooltip=Download oder Abonnement von Kalenderwochen aus ProjectForge. Die Sprache kann unter 'Mein Zugange' geändert werden. +plugins.teamcal.export.weekOfYears.tooltip=Download oder Abonnement von Kalenderwochen aus ProjectForge. Die Sprache kann unter 'Mein Zugange' geändert werden. plugins.teamcal.exportIcsButton=Export ics plugins.teamcal.externalsubscription.label=Externes Abonnement plugins.teamcal.externalsubscription.label.tooltip=Externe Kalender abonnieren (im Ics-Format von Google, Apple icloud etc.) @@ -2038,103 +2038,216 @@ plugins.teamcal.externalsubscription.updateInterval.interval15Min=15 Minuten plugins.teamcal.externalsubscription.updateInterval.interval1d=1 Tag plugins.teamcal.externalsubscription.updateInterval.interval1h=1 Stunde plugins.teamcal.externalsubscription.url=Kalender-URL -plugins.teamcal.externalsubscription.url.tooltip=Unterstützte Urls sind z. B.\nGoogle-Kalender\: https\://www.google.com/calendar/ical/reinhard%40gmail.com/public/basic.ics oder\nApple icloud\: webcal\://p06-calendarws.icloud.com/ca/subscribe/1/... -plugins.teamcal.filterDialog.calendarIsVisible.tooltip=Wenn abgewählt, wird der Kalender nicht angezeigt. Hierüber können kurzfristig Kalender aus- und wieder eingeblendet werden. +plugins.teamcal.externalsubscription.url.tooltip=Unterstützte Urls sind z. B.\nGoogle-Kalender\: https\://www.google.com/calendar/ical/reinhard%40gmail.com/public/basic.ics oder\nApple icloud\: webcal\://p06-calendarws.icloud.com/ca/subscribe/1/... +plugins.teamcal.filterDialog.calendarIsVisible.tooltip=Wenn abgewählt, wird der Kalender nicht angezeigt. Hierüber können kurzfristig Kalender aus- und wieder eingeblendet werden. plugins.teamcal.filterDialog.changeFilter=Filter wechseln plugins.teamcal.fullAccess=Vollzugriff plugins.teamcal.id=ID plugins.teamcal.import.ics.error=Fehler beim Importieren -plugins.teamcal.import.ics.noEventsGiven=Keine Kalendereinträge gefunden. +plugins.teamcal.import.ics.noEventsGiven=Keine Kalendereinträge gefunden. plugins.teamcal.import.ics.title=Ereignisse importieren plugins.teamcal.import.ics.tooltip=Ereignisse im ics-Format importieren. plugins.teamcal.menu=Team-Kalender plugins.teamcal.minimalAccess=Minimalzugriff -plugins.teamcal.minimalAccess.groups.hint=Mitglieder dieser Gruppen können lediglich den Zeitraum eines Eintrags sehen, jedoch keine weiteren Details, wie Ort, Titel etc. -plugins.teamcal.minimalAccess.users.hint=Diese Benutzer:innen können lediglich den Zeitraum eines Eintrags sehen, jedoch keine weiteren Details, wie Ort, Titel etc. +plugins.teamcal.minimalAccess.groups.hint=Mitglieder dieser Gruppen können lediglich den Zeitraum eines Eintrags sehen, jedoch keine weiteren Details, wie Ort, Titel etc. +plugins.teamcal.minimalAccess.users.hint=Diese Benutzer:innen können lediglich den Zeitraum eines Eintrags sehen, jedoch keine weiteren Details, wie Ort, Titel etc. plugins.teamcal.others=andere plugins.teamcal.own=eigene plugins.teamcal.owner=Eigner:in plugins.teamcal.readonlyAccess=Nur lesend -plugins.teamcal.selectCalendar=Ausgewählte Kalender zur Anzeige -plugins.teamcal.selectColor=Farben auswählen -plugins.teamcal.selectTemplate=Filterschablone auswählen +plugins.teamcal.selectCalendar=Ausgewählte Kalender zur Anzeige +plugins.teamcal.selectColor=Farben auswählen +plugins.teamcal.selectTemplate=Filterschablone auswählen plugins.teamcal.subscription=Abonnement -plugins.teamcal.subscription.timesheets=Ãœber diese URL können Sie Ihre Zeitberichte in Ihrem persönlichen Kalender abonnieren (z. B. Apple iCal oder Microsoft Outlook). -plugins.teamcal.subscription.tooltip=Ãœber diese URL können Sie den Kalender in Ihrem persönlichen Kalender abonnieren (z. B. Apple iCal oder Microsoft Outlook). +plugins.teamcal.subscription.timesheets=Ãœber diese URL können Sie Ihre Zeitberichte in Ihrem persönlichen Kalender abonnieren (z. B. Apple iCal oder Microsoft Outlook). +plugins.teamcal.subscription.tooltip=Ãœber diese URL können Sie den Kalender in Ihrem persönlichen Kalender abonnieren (z. B. Apple iCal oder Microsoft Outlook). plugins.teamcal.switchToTeamEventButton=In Termin umwandeln plugins.teamcal.switchToTimesheetButton=In Zeitbuchung umwandeln plugins.teamcal.timeSheetCalendar=Zeitbuchungen plugins.teamcal.title=Titel -plugins.teamcal.title.add=Kalender hinzufügen +plugins.teamcal.title.add=Kalender hinzufügen plugins.teamcal.title.edit=Team-Kalender bearbeiten plugins.teamcal.title.heading=Kalender plugins.teamcal.title.list=Kalenderliste + + + + + + + # poll plugin poll=Umfrage poll.access=Zugriff -poll.answer=Antwort poll.assignment=Zuordnung poll.attendee=Teilnehmer:in poll.attendees=Teilnehmer:innen -poll.button.addQuestion=Eigene Frage hinzufügen +poll.button.addQuestion=Eigene Frage hinzufügen poll.button.finish=Umfrage beenden poll.button.template=Vorlage verwenden -poll.confirmation.creation=Möchtest du die Umfrage wirklich erstellen? Du kannst die Fragen danach nicht mehr bearbeiten.\ -Stelle ebenfalls sicher, dass du Teilnehmer für deine Umfrage hinzugefügt hast. -poll.confirmation.deleteAnswer=Möchtest du diese Antwort wirklich löschen? -poll.confirmation.deleteQuestion=Möchtest du diese Frage wirklich löschen? +poll.confirmation.creation=Möchtest du die Umfrage wirklich erstellen? Du kannst die Fragen danach nicht mehr bearbeiten.\ +Stelle ebenfalls sicher, dass du Teilnehmer für deine Umfrage hinzugefügt hast. +poll.confirmation.deleteAnswer=Möchtest du diese Antwort wirklich löschen? +poll.confirmation.deleteQuestion=Möchtest du diese Frage wirklich löschen? poll.confirmation.finish=Willst du die Umfrage wirklich beenden? + + + + +poll.state=Status poll.date=Datum -poll.deadline=Antwortfrist +poll.deadline=Umfragen Frist poll.delegationAnswers=Antworten von poll.description=Beschreibung + poll.error.oneQuestionRequired=Mindestens eine Frage ist erforderlich. poll.exception.noAttendee=Dieser Nutzer ist nicht Teil der Umfrage. poll.export.response.poll=Ergebnisse exportieren -poll.finished=Beendet poll.fullAccessGroups=Gruppen mit Vollzugriff poll.fullAccessUsers=Benutzer:innen mit Vollzugriff poll.groupAttendees=Teilnehmergruppen poll.guide=Anleitung poll.infopage=Infoseite poll.location=Ort -poll.mail.ended.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ -

wir möchten Sie darüber informieren, dass die Umfrage "{0}", erstellt von {1}, nun abgeschlossen ist. Vielen Dank an alle, die teilgenommen und wertvolles Feedback gegeben haben.

\ -

Falls Sie den Einsendeschluss verpasst haben, möchten wir Sie dennoch ermutigen, uns Ihre Gedanken mitzuteilen. Auch wenn wir Ihre Antworten möglicherweise nicht in den offiziellen Ergebnissen berücksichtigen können, ist Ihr Feedback dennoch wertvoll für zukünftige Umfragen und Initiativen.

\ -

Nochmals vielen Dank für Ihre Teilnahme.

\ -
\ -

Freundliche Grüße,

\ +location=Ort +owner=Ersteller + + + +#Umfrage wurde erstellt +poll.mail.created.subject=Sie wurden zu einer Umfrage eingeladen mit dem Titel "{0}" +poll.mail.created.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ +

Wir möchten Ihnen mitteilen, dass eine Umfrage erstellt wurde mit dem Titel "{0}", und Sie wurden herzlichst eingeladen bei dieser Abzustimmen.

\ +

Hier kommst du direkt zur Umfrage

\ +

Mit Freundlichen Grüßen,

\

{1}

-poll.mail.ended.subject=Umfrage beendet + + + +#Umfrage wurde Bearbeitet +poll.mail.update.subject=Umfrage wurde bearbeitet +poll.mail.update.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ +

Wir möchten Ihnen mitteilen, dass die Umfrage "{0}" kürzlich bearbeitet wurde.

\ +

Falls Sie bereits Ihre Antworten eingereicht haben, sollten Sie überprüfen, ob wesentliche Änderungen vorgenommen wurden.

\ +

Nochmals vielen Dank für Ihre Teilnahme.

\ +

Mit Freundlichen Grüßen,

\ +

{1}

+ + +#Umfrage endet Bald +poll.mail.endingSoon.subject=Umfrage endet in {0} Tagen poll.mail.endingSoon.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ -

wir möchten Sie daran erinnern, dass die Umfrage "{0}", erstellt von {1}, bald endet, nämlich am {2}. Bitte achten Sie darauf, Ihre Antworten vor dem Ablaufdatum einzureichen.

\ +

wir möchten Sie daran erinnern, dass die Umfrage "{0}", erstellt von {1}, bald endet, nämlich am {2}. Bitte achten Sie darauf, Ihre Antworten vor dem Ablaufdatum einzureichen.

\

Falls Sie noch nicht die Gelegenheit hatten, an der Umfrage teilzunehmen, nehmen Sie sich bitte einen Moment Zeit, um dies zu tun, bevor die Umfrage geschlossen wird. Ihre Meinung ist wichtig und wertvoll.

\

{3}

\ -

Vielen Dank für Ihre Aufmerksamkeit und einen schönen Tag!

\ +

Vielen Dank für Ihre Aufmerksamkeit und einen schönen Tag!

\
\ -

Freundliche Grüße,

\ +

Mit Freundlichen Grüßen,

\

{1}

-poll.mail.endingSoon.subject=Umfrage endet in {0} Tagen -poll.mail.update.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ -

Wir möchten Ihnen mitteilen, dass die Umfrage "{0}" kürzlich bearbeitet wurde.

\ -

Falls Sie bereits Ihre Antworten eingereicht haben, sollten Sie überprüfen, ob wesentliche Änderungen vorgenommen wurden.

\ -

Nochmals vielen Dank für Ihre Teilnahme.

\ -

Freundliche Grüße,

\ + + + + +#Umfrage beendet +poll.mail.ended.subject=Die Umfrage mit dem Title "{0}" wurde Beendet +poll.mail.ended.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ +

Wir möchten Sie darüber informieren, dass die Umfrage "{0}", erstellt von {1}, nun abgeschlossen ist. Vielen Dank an alle, die Teilgenommen haben.

\ +

Ihre Ergebnisse/Antworten können sie weiterhin sehen jedoch können sie selber diese nicht mehr bearbeiten.

\ +

Falls Sie aber vergessen haben abzustimmen oder vielleicht noch etwas ändern möchten melden sie sich beim bei einer berechtigten Person oder beim ersteller in diesem Fall "{1}"

\ +
\ +

Mit Freundlichen Grüßen,

\

{1}

-poll.mail.update.subject=Umfrage wurde bearbeitet -poll.manual.multiResponse=Eine Frage, die bei der man eine Antwort auswählen kann. Gut geeignet für eine Datumsumfrage für Verfügbarkeiten. -poll.manual.questions=Anschließend werden die Fragen der Umfrage angelegt. Die Fragen können aus verschiedenen Typen bestehen. -poll.manual.singleResponse=Eine Frage, die bei der man eine Antwort auswählen kann. Beispielsweise für eine simple Ja- oder Nein-Frage. -poll.manual.textQuestion=Eine Frage, die bei der man mit Freitext antworten kann. Gut geeignet für formloses Feedback. -poll.manual.title=Anleitung, um eine Umfrage zu erstellen | Als Erstes muss man die Hauptinformationen der Umfrage ausfüllen. + +#Umfrage abgelaufen +poll.mail.endedafterdeadline.subject=Die Umfrage mit dem Title "{0}" ist Beendet die Deadline ist abgelaufen +poll.mail.endedafterdeadline.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ +

Wir möchten Sie darüber informieren, dass die Umfrage "{0}", erstellt von {1}, nun abgeschlossen ist. Vielen Dank an alle, die Teilgenommen haben.

\ +

Falls Sie vergessen haben abzustimmen oder vielleicht noch etwas ändern möchten melden sie sich beim ersteller der Umfrage in diesem fall "{1}"

\ +

Falls sie sich nicht mehr sicher sind für was sie Abgestimmt haben oder was sie eingetragen haben im Anhang finden sie eine Excel datei mit allen Eingaben

\ +
\ +

Mit Freundlichen Grüßen,

\ +

{1}

+ + + +poll.manual.multiResponse=Eine Frage, die bei der man eine Antwort auswählen kann. Gut geeignet für eine Datumsumfrage für Verfügbarkeiten. +poll.manual.questions=Anschließend werden die Fragen der Umfrage angelegt. Die Fragen können aus verschiedenen Typen bestehen. +poll.manual.singleResponse=Eine Frage, die bei der man eine Antwort auswählen kann. Beispielsweise für eine simple Ja- oder Nein-Frage. +poll.manual.textQuestion=Eine Frage, die bei der man mit Freitext antworten kann. Gut geeignet für formloses Feedback. +poll.manual.title=Anleitung, um eine Umfrage zu erstellen | Als Erstes muss man den poll.other=Andere -poll.owner=Eigentümer:in +poll.owner=Ersteller poll.popup.closed=Umfrage wurde bereits beendet -poll.question=Frage -poll.question.textQuestion=Textfrage -poll.questionType=Fragen Typ + + +poll.question.text=Eingabe +poll.question.single=Frage +poll.question.multi=Frage + + +poll.question.texttitle= Text Frage +poll.question.singletitel= Eine Antwort Frage +poll.question.multititle= Mehrere Antworten Frage + + + +#Kalender Monate +January=Januar +February=Februar +March=März +April=April +May=Mai +June=Juni +July=Juli +August=August +September=September +October=October +November=November +December=Dezember + + + +#Fragen Dropdown menü name +poll.questionType=Fragetypen + +#Frage Typen endung +poll.question.TextQuestion=Antwort +poll.question.SingleResponseQuestion=Frage +poll.question.MultiResponseQuestion=Frage + +#Fragen Container Überschrift +TextQuestion=Frage mit freier text Antwort +SingleResponseQuestion=Frage mit einer Ankreuzmöglichkeit +MultiResponseQuestion=Frage mit mehreren Ankrauzmöglichkeiten + +#Fragen erstellung überschrift +Question=Fragestellung + +#Eingabefeld überschriften/Auswahl möglichkeiten überschrift +poll.answer=Auswahl möglichkeit + +PollState.RUNNING=Am Laufen +question.answers.Yes=Ja +question.answers=Ja +question.answers.0=Ja +question.answers.No=Nein +question.answers.1=Nein + + + + + + poll.respond=Antworten abschicken +yes=Ja +poll.questionType.TextQuestion=Text Frage +poll.questionType.SingleResponseQuestion=Einzelantwort Frage +poll.questionType.MultiResponseQuestion=Mehrfachauswahl Frage + + + + ### not translated: poll.response.mail.update.content=

Dear {0},

\ #

I wanted to inform you that Person {2} has updated their answer to Poll {1}.

\ #

If you were not notified about this,

\ @@ -2145,41 +2258,54 @@ poll.respond=Antworten abschicken ### not translated: poll.response.mail.update.subject=Response was edited by {0} poll.response.page=Seite zur Umfrageantwort poll.response.title=Seite zur Umfrageantwort -poll.running=Aktiv -poll.selectUser=Nutzer auswählen -poll.state=Status +poll.selectUser=Nutzer auswählen + + + poll.title=Titel poll.title.add=Neue Umfrage erstellen poll.title.edit=Umfrage bearbeiten poll.title.list=Umfragen -poll.userDelegation=Für andere Nutzer abstimmen +poll.userDelegation=Für andere Nutzer abstimmen poll.yourAnswers=Deine Antworten + + + + + + + + + + + + projectmanagement.personDays=Personentage projectmanagement.personDays.short=PT -question.deleteQuestion=Soll das Objekt wirklich unwiderruflich gelöscht werden? -question.forceDeleteQuestion=ACHTUNG!!! Soll das Object tatsächlich uwiderruflich gelöscht werden? Es werden auch alle Einträge aus der Änderungshistorie zu diesem Object unwiderruflich gelöscht!!!!!! -question.markAsDeletedQuestion=Soll das Objekt als gelöscht markiert werden? -question.massUpdateQuestion=Sollen nun alle Objekte geändert werden? -scripting.download.filename=Ergbnisdatei -scripting.download.filename.additional=Download nur wenige Minuten verfügbar: bis {0}. -scripting.download.filename.info=Downloaddatei der letzten Scriptausführung. +question.deleteQuestion=Soll das Objekt wirklich unwiderruflich gelöscht werden? +question.forceDeleteQuestion=ACHTUNG!!! Soll das Object tatsächlich unwiderruflich gelöscht werden? Es werden auch alle Einträge aus der Änderungshistorie zu diesem Object unwiderruflich gelöscht!!!!!! +question.markAsDeletedQuestion=Soll das Objekt als gelöscht markiert werden? +question.massUpdateQuestion=Sollen nun alle Objekte geändert werden? +scripting.download.filename=Ergebnisdatei +scripting.download.filename.additional=Download nur wenige Minuten verfügbar: bis {0}. +scripting.download.filename.info=Downloaddatei der letzten Scriptausführung. scripting.myScript.list=Meine Scripte scripting.script=Script -scripting.script.availableVariables=Verfügbare Variablen -scripting.script.description.tooltip=Markdown-Format wird unterstützt. +scripting.script.availableVariables=Verfügbare Variablen +scripting.script.description.tooltip=Markdown-Format wird unterstützt. scripting.script.downloadBackups=Download Backups scripting.script.downloadEffectiveScript=Download Effektivscript -scripting.script.downloadEffectiveScript.info=Das Effektivscript ist das Script mit aufgelösten Include-Dateien etc., welches so von der Scripting-Engine so ausgeführt wird. -scripting.script.editForm.file.tooltip=Die Datei ist im Script über 'script.file' und der Dateiname über 'script.filename' erreichbar. Diese Datei ist aus Abwährtskompatibiltätsgründen noch nutzbar für ältere Scripte. +scripting.script.downloadEffectiveScript.info=Das Effektivscript ist das Script mit aufgelösten Include-Dateien etc., welches so von der Scripting-Engine so ausgeführt wird. +scripting.script.editForm.file.tooltip=Die Datei ist im Script über 'script.file' und der Dateiname über 'script.filename' erreichbar. Diese Datei ist aus Abwährtskompatibiltätsgründen noch nutzbar für ältere Scripte. scripting.script.error.notFound=Script not found. scripting.script.examples=Beispiele scripting.script.executableByGroups=Berechtigte Gruppen -scripting.script.executableByGroups.info=Angehörige dieser Gruppen dürfen dieses Script ausführen (nicht einsehen oder ändern). +scripting.script.executableByGroups.info=Angehörige dieser Gruppen dürfen dieses Script ausführen (nicht einsehen oder ändern). scripting.script.executableByUsers=Berechtigte Benutzer:innen -scripting.script.executableByUsers.info=Diese dürfen dieses Script ausführen (nicht einsehen oder ändern). -scripting.script.execute=Script ausführen -scripting.script.executeAsUser=Als andere:r User:in ausführen -scripting.script.executeAsUser.info=Das Script wird mit ALLEN Rechten dieser oder der Benutzer:in ausgeführt. Bitte vorsichtig verwenden! Im Script selber muss diese Funktion auch aktiviert werden, um Fehlkonfigurationen zu vermeiden. +scripting.script.executableByUsers.info=Diese dürfen dieses Script ausführen (nicht einsehen oder ändern). +scripting.script.execute=Script ausführen +scripting.script.executeAsUser=Als andere:r User:in ausführen +scripting.script.executeAsUser.info=Das Script wird mit ALLEN Rechten dieser oder der Benutzer:in ausgeführt. Bitte vorsichtig verwenden! Im Script selber muss diese Funktion auch aktiviert werden, um Fehlkonfigurationen zu vermeiden. scripting.script.includes=Eingebundene Scripts scripting.script.name=Name scripting.script.parameter=Parameter @@ -2209,16 +2335,16 @@ search.lastHour=Letzte Stunde search.lastHours=Letzte {0} Stunden search.lastMinute=Letzte Minute search.lastMinutes=Letzte {0} Minuten -search.lucene.expression=Modifizierter Ausdruck für Suchmaschine\: -search.maxRowsExceeded=Die Maximalgröße {0} der Ergebnisliste ist überschritten und das Ergebnis enthält nicht alle gefundenen Elemente. -search.nextDays=Nächste {0} Tage +search.lucene.expression=Modifizierter Ausdruck für Suchmaschine\: +search.maxRowsExceeded=Die Maximalgröße {0} der Ergebnisliste ist überschritten und das Ergebnis enthält nicht alle gefundenen Elemente. +search.nextDays=Nächste {0} Tage search.parseError=Fehler beim Vearbeiten des Suchtexts: ''{0}'' -search.periodOfModification=Änderungszeitraum +search.periodOfModification=Änderungszeitraum search.search=Suche search.searchHistory=Historie -search.searchHistory.additional.tooltip=Wenn gewählt, werden auch alle Änderungshistorieneinträge mit dem Suchausdruck durchsucht. +search.searchHistory.additional.tooltip=Wenn gewählt, werden auch alle Änderungshistorieneinträge mit dem Suchausdruck durchsucht. search.sinceYesterday=Seit gestern -search.string.info=''*'' für Teilausdrücke (automatisch angehängt, wenn nur alphanumerische Ausdrücke angegeben inkl. Leerzeichen und "@._-"), ''-ausdruck'' für Ausschlüsse, ''Feldname\:Ausdruck'' für Teilwortsuche, s. auch Dokumentation (es wird Lucene als Suchmaschine eingesetzt).\nSuche in den Feldern\: {0}. +search.string.info=''*'' für Teilausdrücke (automatisch angehängt, wenn nur alphanumerische Ausdrücke angegeben inkl. Leerzeichen und "@._-"), ''-ausdruck'' für Ausschlüsse, ''Feldname\:Ausdruck'' für Teilwortsuche, s. auch Dokumentation (es wird Lucene als Suchmaschine eingesetzt).\nSuche in den Feldern\: {0}. search.string.info.title=Volltextsuche search.title=Suche search.today=Heute @@ -2246,7 +2372,7 @@ space.title.edit=Space bearbeiten space.title.heading=Spaces space.title.list=Spaces system.admin.adminLogViewer.title=Adminprotokoll einsehen -system.admin.alertMessage.copyAndPaste.text=Achtung\: ProjectForge ist um 13\:00 Uhr für ca. 5 Minuten aufgrund von Wartungsarbeiten nicht erreichbar\! Es wird das neue Release {0} eingespielt. +system.admin.alertMessage.copyAndPaste.text=Achtung\: ProjectForge ist um 13\:00 Uhr für ca. 5 Minuten aufgrund von Wartungsarbeiten nicht erreichbar\! Es wird das neue Release {0} eingespielt. ### not translated: system.admin.alertMessage.copyAndPaste.title=For copy & paste ### not translated: system.admin.button.checkI18nProperties=Check i18n properties ### not translated: system.admin.button.checkI18nProperties.tooltip=Check i18n properties for detecting missing translations in different languages. @@ -2308,26 +2434,26 @@ system.admin.logViewer.userAgent=Browser ### not translated: system.pluginAdmin.title=Plugins ### not translated: system.statistics.databasePool=Data base pool system.statistics.title=Systemstatistiken -system.statistics.totalNumberOfHistoryEntries=Gesamtzahl aller Historierungseinträge +system.statistics.totalNumberOfHistoryEntries=Gesamtzahl aller Historierungseinträge system.statistics.totalNumberOfTasks=Gesamtzahl aller Strukturelemente system.statistics.totalNumberOfTimesheets=Gesamtzahl der Zeitberichte system.statistics.totalNumberOfUsers=Gesamtzahl der Benutzer:innen -system.statistics.totalTimesheetDurations=Gesamtdauer über alle Zeitberichte [Tage] +system.statistics.totalTimesheetDurations=Gesamtdauer über alle Zeitberichte [Tage] task.assignedUser=Verantwortliche:r task.consumption=Verbrauch -task.deleted=gelöscht -task.edit.maxHoursIngoredDueToAssignedOrders=Diese Angabe wird ggf. ignoriert. - Zu diesem Strukturelement oder einem Strukturunterelement wurden Aufträge zugeordnet. Die Angabe einer Null verhindert jedoch die Anzeige des Maximalwerts in den Verbrauchsbalken. -task.error.couldNotDeleteRootTask=Das Wurzelstrukturelement kann nicht gelöscht werden! -task.error.cyclicReference=Zyklische Strukturhierarchie entdeckt, weil ein Strukturelement sich selbst oder ein Strukturunterelement als übergeordnetes Strukturelement besitzt. -task.error.duplicateChildTasks=Strukturelemente müssen als Unterelemente unterschiedliche Titel haben. +task.deleted=gelöscht +task.edit.maxHoursIngoredDueToAssignedOrders=Diese Angabe wird ggf. ignoriert. - Zu diesem Strukturelement oder einem Strukturunterelement wurden Aufträge zugeordnet. Die Angabe einer Null verhindert jedoch die Anzeige des Maximalwerts in den Verbrauchsbalken. +task.error.couldNotDeleteRootTask=Das Wurzelstrukturelement kann nicht gelöscht werden! +task.error.cyclicReference=Zyklische Strukturhierarchie entdeckt, weil ein Strukturelement sich selbst oder ein Strukturunterelement als übergeordnetes Strukturelement besitzt. +task.error.duplicateChildTasks=Strukturelemente müssen als Unterelemente unterschiedliche Titel haben. task.error.kost2Readonly=Eine Kost2-Zuordnung kann nur durch die Buchhaltung vorgenommen werden. -task.error.parentTaskNotFound=Ãœbergeordnetes Strukturelement wird benötigt, konnte aber nicht gefunden werden. +task.error.parentTaskNotFound=Ãœbergeordnetes Strukturelement wird benötigt, konnte aber nicht gefunden werden. task.error.parentTaskNotGiven=Ãœbergeordnetes Strukturelement muss angegeben sein. -task.error.protectionOfPrivacyReadonly=Die Datenschutzoption kann nur durch die Buchhaltung geändert werden. +task.error.protectionOfPrivacyReadonly=Die Datenschutzoption kann nur durch die Buchhaltung geändert werden. task.error.protectTimesheetsUntilReadonly=Der Zeitberichtsschutz kann nur durch die Buchhaltung manipuliert werden. -task.error.timesheetBookingStatus2Readonly=Der Status für Zeitberichtsbuchungen kann nur durch die Buchhaltung oder Projektmanager manipuliert werden. +task.error.timesheetBookingStatus2Readonly=Der Status für Zeitberichtsbuchungen kann nur durch die Buchhaltung oder Projektmanager manipuliert werden. task.favorite.new=Neuer Strukturelementfavorit -task.favorite.new.tooltip=Das aktuell ausgewählte Strukturelement kann hier unter einem Namen als Favorit gespeichert werden. +task.favorite.new.tooltip=Das aktuell ausgewählte Strukturelement kann hier unter einem Namen als Favorit gespeichert werden. task.favorites.tooltip=Verwaltung von Strukturelementen als Favoriten task.gantt.settings=Ganttwerte task.kost2list.blackList=Negativliste @@ -2341,17 +2467,17 @@ task.menu.showTimesheets=Zeitberichte anzeigen task.onlyBillable=nur fakturierbare Elemente task.parentTask=Ãœbergeordnetes Strukturelement task.path=Strukturhierarchie -task.path.pleaseSelectTask=Bitte Strukturelement wählen +task.path.pleaseSelectTask=Bitte Strukturelement wählen task.path.rootTask=Toplevel task.progress=Fortschritt task.protectionOfPrivacy=Datenschutz -task.protectionOfPrivacy.tooltip=Wenn diese Option gewählt wird, dann kann nicht auf die Zeitberichte anderer Benutzer:innen unterhalb dieses Tasks zugegriffen werden. +task.protectionOfPrivacy.tooltip=Wenn diese Option gewählt wird, dann kann nicht auf die Zeitberichte anderer Benutzer:innen unterhalb dieses Tasks zugegriffen werden. task.protectTimesheetsUntil=Zeitberichtsschutz bis task.protectTimesheetsUntil.short=Schutz bis task.recursive=inkl. aller Strukturunterelemente task.reference=Referenz task.selectPanel.displayTask.tooltip=Strukturelement anzeigen. -task.selectPanel.info=Ordnerelement können ausgewählt werden, indem in eine andere Spalte geklickt wird (außer der ersten). +task.selectPanel.info=Ordnerelement können ausgewählt werden, indem in eine andere Spalte geklickt wird (außer der ersten). task.selectPanel.noTasksFound=Keine Strukturelemente gefunden. task.selectPanel.selectAncestorTask.tooltip=Strukturelement durch dieses Strukturoberelement ersetzen. task.status=Status des Strukturelements @@ -2362,79 +2488,79 @@ task.tasks=Strukturelemente task.timesheetBooking=Zeitbuchungen task.timesheetBooking.inherit=vererbt task.timesheetBooking.noBooking=keine Buchungen -task.timesheetBooking.onlyLeafs=nur Strukturelementblätter -task.timesheetBooking.opened=geöffnet +task.timesheetBooking.onlyLeafs=nur Strukturelementblätter +task.timesheetBooking.opened=geöffnet task.timesheetBooking.treeClosed=komplett geschlossen task.title=Titel task.title.add=Neues Strukturelement task.title.edit=Strukturelement bearbeiten task.title.heading=Strukturelemente task.title.list=Strukturelemente -task.title.list.select=Strukturelement auswählen +task.title.list.select=Strukturelement auswählen task.tree.close=Strukturelement zuklappen task.tree.explore=Alle Strukturelemente auf-/zuklappen -task.tree.info=Strukturelemente können auf- und zugeklappt werden, indem auf die Ordnersymbole oder Titel des gewünschten Strukturelements geklickt wird. Zur Auswahl eines Strukturelements kann jede andere Stelle innerhalb der Zeile angeklickt werden. +task.tree.info=Strukturelemente können auf- und zugeklappt werden, indem auf die Ordnersymbole oder Titel des gewünschten Strukturelements geklickt wird. Zur Auswahl eines Strukturelements kann jede andere Stelle innerhalb der Zeile angeklickt werden. task.tree.open=Strukturelement aufklappen task.tree.perspective=Baumansicht task.tree.rootNode=Wurzelstrukturelement task.tree.title=Strukturbaum -task.tree.title.select=Strukturelement auswählen +task.tree.title.select=Strukturelement auswählen task.wizard.action=Aktion -task.wizard.action.noactionRequired=Keine Aktion notwendig, da keine Gruppen ausgewählt wurden. Demnach müssen keine Zugriffsrechte angelegt werden. -task.wizard.action.taskAndgroupsGiven=Die notwendigen Zugriffsrechte für die oben angegebene(n) Gruppe(n) werden angelegt, nachdem der Anlegen-Button gedrückt wird. Diese können Sie jederzeit unter dem Menüpunkt Zugriffsverwaltung einsehen und ändern. +task.wizard.action.noactionRequired=Keine Aktion notwendig, da keine Gruppen ausgewählt wurden. Demnach müssen keine Zugriffsrechte angelegt werden. +task.wizard.action.taskAndgroupsGiven=Die notwendigen Zugriffsrechte für die oben angegebene(n) Gruppe(n) werden angelegt, nachdem der Anlegen-Button gedrückt wird. Diese können Sie jederzeit unter dem Menüpunkt Zugriffsverwaltung einsehen und ändern. task.wizard.button.createGroup=Gruppe anlegen -task.wizard.button.createGroup.tooltip=Sie können eine existierende Benutzergruppe wählen oder eine neue durch Anklicken dieses Buttons anlegen. +task.wizard.button.createGroup.tooltip=Sie können eine existierende Benutzergruppe wählen oder eine neue durch Anklicken dieses Buttons anlegen. task.wizard.button.createTask=Strukturelement anlegen -task.wizard.button.createTask.tooltip=Sie können ein existierendes Strukturelement wählen oder ein neues durch Anklicken dieses Buttons anlegen. +task.wizard.button.createTask.tooltip=Sie können ein existierendes Strukturelement wählen oder ein neues durch Anklicken dieses Buttons anlegen. task.wizard.finish=Fertig stellen -task.wizard.intro=Benutzen Sie diesen Assistenten, um ein neues Strukturelement anzulegen. Dieses Strukturelement kann ein Projekt o. ä. repräsentieren auf welche Nutzer und Gruppen Zugriff haben sollen (z. B. für Zeitberichtserfassungen). +task.wizard.intro=Benutzen Sie diesen Assistenten, um ein neues Strukturelement anzulegen. Dieses Strukturelement kann ein Projekt o. ä. repräsentieren auf welche Nutzer und Gruppen Zugriff haben sollen (z. B. für Zeitberichtserfassungen). task.wizard.managerGroup=Teammanager:in (optional) task.wizard.managerGroup.groupNameSuffix=Projektmanagement -task.wizard.managerGroup.intro=Die Teammanager:innen haben mehr Rechte, so dürfen sie beispielsweise die Zeitberichte anderer Nutzer ändern. Wenn sie die Rolle Projektmanagement (Gruppe PF_MANAGER) haben, dürfen sie auch HR-Planungen vornehmen. +task.wizard.managerGroup.intro=Die Teammanager:innen haben mehr Rechte, so dürfen sie beispielsweise die Zeitberichte anderer Nutzer ändern. Wenn sie die Rolle Projektmanagement (Gruppe PF_MANAGER) haben, dürfen sie auch HR-Planungen vornehmen. task.wizard.pageTitle=Strukturassistent -task.wizard.task.intro=Für dieses Strukturelement sollen die Rechte automatisch für die unten angegebenen Gruppen eingerichtet werden. Dabei wird für alle Oberelemente ein minimales Leserecht, so dass aus der Baumansicht dieses Element für die Gruppen eingesehen werden kann. Andere Elemente aus höheren Ebenen bleiben unsichtbar. +task.wizard.task.intro=Für dieses Strukturelement sollen die Rechte automatisch für die unten angegebenen Gruppen eingerichtet werden. Dabei wird für alle Oberelemente ein minimales Leserecht, so dass aus der Baumansicht dieses Element für die Gruppen eingesehen werden kann. Andere Elemente aus höheren Ebenen bleiben unsichtbar. task.wizard.team=Team (optional) -task.wizard.team.intro=Die Teammitglieder haben das Recht, Strukturunterelemente anzulegen oder zu ändern, sowie eigene Zeitberichte zu erfassen. +task.wizard.team.intro=Die Teammitglieder haben das Recht, Strukturunterelemente anzulegen oder zu ändern, sowie eigene Zeitberichte zu erfassen. timePeriodPanel.startTimeAfterStopTime=Der Beginn muss vor dem Ende liegen. timesheet=Zeitbericht timesheet.break=leer -timesheet.description=Tätigkeitsbericht +timesheet.description=Tätigkeitsbericht timesheet.duration=Dauer -timesheet.error.copyNoMatchingKost2=Keine Kost2 Id für diese Zeiberichtskopie verfügbar oder keine übereinstimmende mit dem Orginal gefunden. Wahrscheinlich wurde das Strukturelement des Zeitberichts in einen anderen Unterbaum verschoben. Zur Lösung des Problems bitte manuell Kost2 Id wählen. +timesheet.error.copyNoMatchingKost2=Keine Kost2 Id für diese Zeiberichtskopie verfügbar oder keine übereinstimmende mit dem Orginal gefunden. Wahrscheinlich wurde das Strukturelement des Zeitberichts in einen anderen Unterbaum verschoben. Zur Lösung des Problems bitte manuell Kost2 Id wählen. timesheet.error.filter.needMore=Zu wenige Filterangaben: Bitte Datum und/oder Strukturelement angeben. -timesheet.error.invalidKost2=Keine gültige Kost2 Id. +timesheet.error.invalidKost2=Keine gültige Kost2 Id. timesheet.error.invalidTaskId=Das Strukturelement wurde leider nicht gefunden. -timesheet.error.kost2NeededChooseSubTask=Bitte Kost2 auswählen und ggf. Strukturunterelement mit Kost2s bebuchen. -timesheet.error.kost2Required=Bitte Kost2 auswählen. -timesheet.error.massupdate.couldnotconvertkost2=Massenänderung für Zeitbericht nicht möglich. Das Ziel-Strukturelement hat mehrere Kost2-Einträge aber es gibt keine passende Kost2-Art. -timesheet.error.massupdate.kost2notsupported=Massenänderung für Zeitbericht nicht möglich. Das Ziel-Strukturelement unterstützt nicht die gewählte Kost2. -timesheet.error.massupdate.kost2null=Massenänderung für Zeitbericht nicht möglich. Für das Ziel-Strukturelement wird eine Kost2 benötigt. -timesheet.error.maximumDurationExceeded=Maximal zugelassene Dauer überschritten. -timesheet.error.noAccess=Fehlende Berechtigung für die ausgewählte Aktion. -timesheet.error.overlapping=Die ausgewählte Aktion ist aufgrund einer zeitlichen Ãœberschneidung nicht möglich. -timesheet.error.taskNotBookable.onlyLeafsAllowedForBooking=Das Strukturelement {0} kann nicht bebucht werden, da nur Strukturblätterelemente (d. h. Strukturelemente ohne Unterelementen) bebucht werden können. +timesheet.error.kost2NeededChooseSubTask=Bitte Kost2 auswählen und ggf. Strukturunterelement mit Kost2s bebuchen. +timesheet.error.kost2Required=Bitte Kost2 auswählen. +timesheet.error.massupdate.couldnotconvertkost2=Massenänderung für Zeitbericht nicht möglich. Das Ziel-Strukturelement hat mehrere Kost2-Einträge aber es gibt keine passende Kost2-Art. +timesheet.error.massupdate.kost2notsupported=Massenänderung für Zeitbericht nicht möglich. Das Ziel-Strukturelement unterstützt nicht die gewählte Kost2. +timesheet.error.massupdate.kost2null=Massenänderung für Zeitbericht nicht möglich. Für das Ziel-Strukturelement wird eine Kost2 benötigt. +timesheet.error.maximumDurationExceeded=Maximal zugelassene Dauer überschritten. +timesheet.error.noAccess=Fehlende Berechtigung für die ausgewählte Aktion. +timesheet.error.overlapping=Die ausgewählte Aktion ist aufgrund einer zeitlichen Ãœberschneidung nicht möglich. +timesheet.error.taskNotBookable.onlyLeafsAllowedForBooking=Das Strukturelement {0} kann nicht bebucht werden, da nur Strukturblätterelemente (d. h. Strukturelemente ohne Unterelementen) bebucht werden können. timesheet.error.taskNotBookable.orderPositionsFoundInSubTasks=Das Strukturelement {0} kann nicht bebucht werden, da Auftragszuordnungen in Unterelementen existieren. -timesheet.error.taskNotBookable.taskClosedForBooking=Das Strukturelement {0} kann nicht bebucht werden, da es für Zeitberichtsbuchungen geschlossen ist. -timesheet.error.taskNotBookable.taskDeleted=Das Strukturelement {0} kann nicht bebucht werden, da es gelöscht ist. -timesheet.error.taskNotBookable.taskNotOpened=Das Strukturelement {0} kann nicht bebucht werden, da es nicht geöffnet ist. -timesheet.error.taskNotBookable.treeClosedForBooking=Das Strukturelement {0} kann nicht bebucht werden, da der komplette Strukturbaum für Zeitberichtsbuchungen geschlossen ist. +timesheet.error.taskNotBookable.taskClosedForBooking=Das Strukturelement {0} kann nicht bebucht werden, da es für Zeitberichtsbuchungen geschlossen ist. +timesheet.error.taskNotBookable.taskDeleted=Das Strukturelement {0} kann nicht bebucht werden, da es gelöscht ist. +timesheet.error.taskNotBookable.taskNotOpened=Das Strukturelement {0} kann nicht bebucht werden, da es nicht geöffnet ist. +timesheet.error.taskNotBookable.treeClosedForBooking=Das Strukturelement {0} kann nicht bebucht werden, da der komplette Strukturbaum für Zeitberichtsbuchungen geschlossen ist. timesheet.error.timeperiodOverlapDetection=Der Zeitbericht kollidiert mit dem Zeitbericht #{0} (gleiche:r Benutzer:in) von {1} bis {2}. -timesheet.error.timesheetProtectionVioloation=Der Zeitbericht verletzt den Zeitberichtsschutz des Strukturelements ''{0}'', welcher bis einschließlich {1} gesetzt ist. Bitte Rücksprache mit der Buchhaltung nehmen. +timesheet.error.timesheetProtectionVioloation=Der Zeitbericht verletzt den Zeitberichtsschutz des Strukturelements ''{0}'', welcher bis einschließlich {1} gesetzt ist. Bitte Rücksprache mit der Buchhaltung nehmen. timesheet.error.zeroDuration=Der Zeitbericht hat keine Dauer. timesheet.filter.withTimeperiodCollision=Nur kollidierte -timesheet.filter.withTimeperiodCollision.tooltip=Es werden nur Zeitberichte angezeigt, die mit anderen Zeitberichten (gleiche:r Nutzer:in) kollidieren (sich überlappen). -timesheet.iCalSubscription=Zeitberichte im iCal-Format für den/die angemeldete:n Benutzer:in exportieren (*.ics). +timesheet.filter.withTimeperiodCollision.tooltip=Es werden nur Zeitberichte angezeigt, die mit anderen Zeitberichten (gleiche:r Nutzer:in) kollidieren (sich überlappen). +timesheet.iCalSubscription=Zeitberichte im iCal-Format für den/die angemeldete:n Benutzer:in exportieren (*.ics). timesheet.icsExport=ics-Export timesheet.location=Ort -timesheet.location.tooltip=Einträge aus der Vorschlagsliste können entfernt werden, indem einfach das Entfernen-Symbol (Kreuz) am Ende der Zeile des zu löschenden Orts geklickt wird. Entfernte Orte können wieder aufgenommen werden, indem sie einfach wieder erneut verwendet werden. -timesheet.massupdate.kost.info=Wenn Zeitberichte eines Projekts zu einem anderen transferiert werden, wird der Kostenträger automatisch migriert, falls möglich, d. h. die Kostenart (letzte beiden Ziffern) bleibt erhalten. -timesheet.massupdate.updateTask=Strukturelement für alle Zeitberichte ändern +timesheet.location.tooltip=Einträge aus der Vorschlagsliste können entfernt werden, indem einfach das Entfernen-Symbol (Kreuz) am Ende der Zeile des zu löschenden Orts geklickt wird. Entfernte Orte können wieder aufgenommen werden, indem sie einfach wieder erneut verwendet werden. +timesheet.massupdate.kost.info=Wenn Zeitberichte eines Projekts zu einem anderen transferiert werden, wird der Kostenträger automatisch migriert, falls möglich, d. h. die Kostenart (letzte beiden Ziffern) bleibt erhalten. +timesheet.massupdate.updateTask=Strukturelement für alle Zeitberichte ändern timesheet.multiselected.title=Mehrfachauswahl Zeitberichte timesheet.recent=Zuletzt verwendet timesheet.recent.select=Zuletzt bearbeitete Berichte zur Auswahl timesheet.recenttasks.select=--- Zuletzt verwendete Strukturelemente --- timesheet.reference=Referenz -timesheet.reference.info=Referenzen können zur Gruppierung von Zeitberichten genutzt werden. Bereits benutzte Referenzen in Zeitberichten in der Hierarchie des Strukturelements (auch von anderen Usern) können per Autovervollständigung genutzt werden. +timesheet.reference.info=Referenzen können zur Gruppierung von Zeitberichten genutzt werden. Bereits benutzte Referenzen in Zeitberichten in der Hierarchie des Strukturelements (auch von anderen Usern) können per Autovervollständigung genutzt werden. timesheet.signatureEmployee=Unterschrift Mitarbeiter:in timesheet.signatureProjectLeader=Unterschrift Projektmanagement timesheet.startTime=Beginn @@ -2443,10 +2569,10 @@ timesheet.tag=Etikett timesheet.taskReference=Elementreferenz timesheet.templates=Vorlagen timesheet.templates.migrationOfLegacy.button=Alte Vorlagen -timesheet.templates.migrationOfLegacy.confirmationMessage=Sollen alte Vorlagen jetzt übernommen werden? Vorhandene Vorlagen werden nicht überschrieben. +timesheet.templates.migrationOfLegacy.confirmationMessage=Sollen alte Vorlagen jetzt übernommen werden? Vorhandene Vorlagen werden nicht überschrieben. timesheet.templates.migrationOfLegacy.tooltip=Ãœbernehmen alter Vorlagen der klassischen Version. timesheet.templates.new=Neue Vorlage -timesheet.templates.new.tooltip=Du kannst dieses Formular ausfüllen und anschließend als Vorlage definieren, indem du hier einen Namen vergibst. +timesheet.templates.new.tooltip=Du kannst dieses Formular ausfüllen und anschließend als Vorlage definieren, indem du hier einen Namen vergibst. timesheet.timesheets=Zeitberichte timesheet.title.add=Neuer Zeitbericht timesheet.title.edit=Zeitbericht bearbeiten @@ -2454,43 +2580,43 @@ timesheet.title.heading=Zeitberichte timesheet.title.list=Zeitberichte timesheet.totalDuration=Gesamtdauer timesheet.user=Mitarbeiter:in -tooltip.accesskey.addEntry=Je nach Browser\: STRG-N, ALT-N, ALT-Shift-N etc. drücken -tooltip.accesskey.addEntry.title=Tastaturkürzel N -tooltip.assign=Selektierte Einträge zuweisen +tooltip.accesskey.addEntry=Je nach Browser\: STRG-N, ALT-N, ALT-Shift-N etc. drücken +tooltip.accesskey.addEntry.title=Tastaturkürzel N +tooltip.assign=Selektierte Einträge zuweisen tooltip.autocomplete.language=Ãœber einen Doppelklick auf das Feld werden auch alle von allen Benutzer:innen bisher verwendeten Sprachen angezeigt. -tooltip.autocomplete.recentSearchTerms=Ãœber einen Doppelklick auf das Feld werden die zuletzt verwendeten Suchausdrücke zur Auswahl angezeigt. +tooltip.autocomplete.recentSearchTerms=Ãœber einen Doppelklick auf das Feld werden die zuletzt verwendeten Suchausdrücke zur Auswahl angezeigt. tooltip.autocomplete.timeZone=Ãœber einen Doppelklick auf das Feld werden auch alle von allen Benutzer:innen bisher verwendeten Zeitzonen angezeigt. tooltip.autocomplete.withDblClickFunction=Ãœber einen Doppelklick auf das Feld werden die Favoriten zur Auswahl angezeigt. -tooltip.autocompletion.title=Autovervollständigung +tooltip.autocompletion.title=Autovervollständigung tooltip.cancel=Abbrechen -tooltip.entry.delete=Eintrag löschen -tooltip.entry.markAsDeleted=Eintrag als gelöscht markieren +tooltip.entry.delete=Eintrag löschen +tooltip.entry.markAsDeleted=Eintrag als gelöscht markieren tooltip.entry.undelete=Eintrag wiederherstellen tooltip.export.excel=Export im Excelformat tooltip.export.pdf=Export als pdf tooltip.hideBirthdays=Geburtstage ausblenden -tooltip.jiraSupport.field.content=Es werden JIRA-Issues von diesem Feld unterstützt, d. h. JIRA-Issues der Form PROJECTFORGE-123 werden als JIRA-Link angezeigt. +tooltip.jiraSupport.field.content=Es werden JIRA-Issues von diesem Feld unterstützt, d. h. JIRA-Issues der Form PROJECTFORGE-123 werden als JIRA-Link angezeigt. tooltip.jiraSupport.field.title=JIRA-Support tooltip.lucene.link=Link zur Lucene-Abfrage-Syntax. -tooltip.popups.mustBeAllowed=Bitte beachten: Für diese Funktion müssen Popups in Ihrem Browser erlaubt sein. +tooltip.popups.mustBeAllowed=Bitte beachten: Für diese Funktion müssen Popups in Ihrem Browser erlaubt sein. tooltip.reload=Neu laden -tooltip.selectBirthday=Geburtstag wählen -tooltip.selectDate=Datum wählen -tooltip.selectDateOrPeriod=Datum oder Zeitraum wählen -tooltip.selectGroup=Gruppe wählen +tooltip.selectBirthday=Geburtstag wählen +tooltip.selectDate=Datum wählen +tooltip.selectDateOrPeriod=Datum oder Zeitraum wählen +tooltip.selectGroup=Gruppe wählen tooltip.selectMe=You are great! -tooltip.selectTask=Strukturelement wählen -tooltip.selectUser=Benutzer:in wählen -tooltip.unassign=Zuweisung für selektierte Einträge aufheben +tooltip.selectTask=Strukturelement wählen +tooltip.selectUser=Benutzer:in wählen +tooltip.unassign=Zuweisung für selektierte Einträge aufheben tooltip.unselect=Auswahl aufheben -tooltip.unselectBirthday=Geburtstag löschen +tooltip.unselectBirthday=Geburtstag löschen tooltip.unselectDate=Datumswahl aufheben tooltip.unselectGroup=Gruppenauswahl aufheben tooltip.unselectTask=Strukturelementwahl aufheben tooltip.unselectUser=Benutzer:innenwahl aufheben -tutorial.expectedGroupNotFound=Die benötigte Gruppe ''{0}'' ist noch nicht angelegt. Die Tutorialanfrage kann deshalb nicht bearbeitet werden. -tutorial.expectedTaskNotFound=Das benötigte Strukturelement ''{0}'' ist noch nicht angelegt. Die Tutorialanfrage kann deshalb nicht bearbeitet werden. -tutorial.expectedUserNotFound=Der benötigte Benutzer ''{0}'' ist noch nicht angelegt. Die Tutorialanfrage kann deshalb nicht bearbeitet werden. +tutorial.expectedGroupNotFound=Die benötigte Gruppe ''{0}'' ist noch nicht angelegt. Die Tutorialanfrage kann deshalb nicht bearbeitet werden. +tutorial.expectedTaskNotFound=Das benötigte Strukturelement ''{0}'' ist noch nicht angelegt. Die Tutorialanfrage kann deshalb nicht bearbeitet werden. +tutorial.expectedUserNotFound=Der benötigte Benutzer ''{0}'' ist noch nicht angelegt. Die Tutorialanfrage kann deshalb nicht bearbeitet werden. tutorial.objectAlreadyCreated=Die Tutorialanfrage kann nicht bearbeitet werden, da das angefragte Objekt bereits existiert. tutorial.unknown=Die Tutorialanfrage kann nicht bearbeitet werden. user.activated=Aktiviert @@ -2499,46 +2625,46 @@ user.add.optionalPassword=Hier kann optional ein Erstpasswort vergeben werden. user.adminUsers=Administrator:innen user.adminUsers.none=Nichtadministrator:innen user.assignedGroups=Assoziierte Gruppen -user.authenticationToken.authenticator_key=Schlüssel für Zweifaktor-Authentifizierung -user.authenticationToken.authenticator_key.tooltip=Dieser Schlüssel kann für die Zweifaktor-Authentifizierung z. B. über die Apps Microsoft-, Google Authenticator oder Fortitoken benutzt werden. +user.authenticationToken.authenticator_key=Schlüssel für Zweifaktor-Authentifizierung +user.authenticationToken.authenticator_key.tooltip=Dieser Schlüssel kann für die Zweifaktor-Authentifizierung z. B. über die Apps Microsoft-, Google Authenticator oder Fortitoken benutzt werden. user.authenticationToken.button.showUsage=Info -user.authenticationToken.button.showUsage.tooltip=Benutzung dieses Schüssels -user.authenticationToken.calendar_rest=Schlüssel für Kalenderexport -user.authenticationToken.calendar_rest.tooltip=Dieser persönliche Schlüssel kann zum Export/Abonnement von Kalendern oder Zeitberichten verwendet werden. -user.authenticationToken.dav_token=Schlüssel für CalDav/CardDAV -user.authenticationToken.dav_token.tooltip=Dieser persönliche Schlüssel kann zur Benutzung der CardDAV/CalDAV-Funktion verwendet werden. +user.authenticationToken.button.showUsage.tooltip=Benutzung dieses Schüssels +user.authenticationToken.calendar_rest=Schlüssel für Kalenderexport +user.authenticationToken.calendar_rest.tooltip=Dieser persönliche Schlüssel kann zum Export/Abonnement von Kalendern oder Zeitberichten verwendet werden. +user.authenticationToken.dav_token=Schlüssel für CalDav/CardDAV +user.authenticationToken.dav_token.tooltip=Dieser persönliche Schlüssel kann zur Benutzung der CardDAV/CalDAV-Funktion verwendet werden. user.authenticationToken.renew=Erneuern -user.authenticationToken.renew.securityQuestion=Soll der Schlüssel wirklich unwiderrufbar erneuert werden? -user.authenticationToken.renew.successful=Der Schlüssel wurde erneuert und ist ab sofort gültig. -user.authenticationToken.renew.tooltip=Der Schlüssel kann erneuert werden, wenn der Verdacht auf einen möglichen Missbrauch vorliegt. Nach der Erneuerung sind vorher benutzte Schlüssel ungültig. Bitte beachten: Diese Operation ist unwiderruflich! -user.authenticationToken.rest_client=Schlüssel für Rest-Clients -user.authenticationToken.rest_client.tooltip=Dieser persönliche Schlüssel kann zur Benutzung in Rest-Clients verwendet werden, wenn das ProjectForge-Passwort dort nicht verwendet werden soll. -user.authenticationToken.stay_logged_in_key=Schlüssel für Angemeldet-Bleiben-Funktion -user.authenticationToken.stay_logged_in_key.tooltip=Dieser persönliche Schlüssel wird für die Angemeldet-Bleiben-Funktion im Browser verwendet und als Cookie im Browser gespeichert. Durch Erneuern können alle Browsersitzungen beendet werden. +user.authenticationToken.renew.securityQuestion=Soll der Schlüssel wirklich unwiderrufbar erneuert werden? +user.authenticationToken.renew.successful=Der Schlüssel wurde erneuert und ist ab sofort gültig. +user.authenticationToken.renew.tooltip=Der Schlüssel kann erneuert werden, wenn der Verdacht auf einen möglichen Missbrauch vorliegt. Nach der Erneuerung sind vorher benutzte Schlüssel ungültig. Bitte beachten: Diese Operation ist unwiderruflich! +user.authenticationToken.rest_client=Schlüssel für Rest-Clients +user.authenticationToken.rest_client.tooltip=Dieser persönliche Schlüssel kann zur Benutzung in Rest-Clients verwendet werden, wenn das ProjectForge-Passwort dort nicht verwendet werden soll. +user.authenticationToken.stay_logged_in_key=Schlüssel für Angemeldet-Bleiben-Funktion +user.authenticationToken.stay_logged_in_key.tooltip=Dieser persönliche Schlüssel wird für die Angemeldet-Bleiben-Funktion im Browser verwendet und als Cookie im Browser gespeichert. Durch Erneuern können alle Browsersitzungen beendet werden. user.changePassword.error.noCharacter=Passwort muss mindestens einen Buchstaben haben. user.changePassword.error.noNonCharacter=Passwort muss mindestens einen Nicht-Buchstaben haben. user.changePassword.error.notMinLength=Passwort muss mindestens {0} Zeichen haben. user.changePassword.error.oldPasswdEqualsNew=Neues Passwort darf nicht dem alten entsprechen. user.changePassword.error.oldPasswordWrong=Das alte Passwort wurde nicht korrekt eingegeben. user.changePassword.error.passwordQualityCheck=Passwort muss mindestens {0} Zeichen und sowohl mindestens einen Buchstaben als auch einen Nicht-Buchstaben enthalten. -user.changePassword.msg.passwordSuccessfullyChanged=Passwort wurde erfolgreich geändert. +user.changePassword.msg.passwordSuccessfullyChanged=Passwort wurde erfolgreich geändert. user.changePassword.newPassword=Neues Passwort -user.changePassword.notSupported=Die Passwortänderung wird nicht unterstützt. Kontaktieren Sie Ihre:n Administrator:in. +user.changePassword.notSupported=Die Passwortänderung wird nicht unterstützt. Kontaktieren Sie Ihre:n Administrator:in. user.changePassword.oldPassword=Altes Passwort -user.changePassword.password.lastChange=Letzte Passwortänderung -user.changePassword.title=Eigenes Passwort ändern +user.changePassword.password.lastChange=Letzte Passwortänderung +user.changePassword.title=Eigenes Passwort ändern user.changeWlanPassword.error.loginPasswordWrong=Das Login-Passwort wurde nicht korrekt eingegeben. -user.changeWlanPassword.lastChange=Letzte WLAN/Samba-Passwortänderung +user.changeWlanPassword.lastChange=Letzte WLAN/Samba-Passwortänderung user.changeWlanPassword.loginPassword=Login-Passwort user.changeWlanPassword.newPassword=Neues WLAN-Passwort -user.changeWlanPassword.title=WLAN/Samba Passwort ändern +user.changeWlanPassword.title=WLAN/Samba Passwort ändern user.deactivated=Deaktiviert user.defaultLocale=Default (Browser) -user.error.passwordAndRepeatDoesNotMatch=Passwort und Passwortwiederholung stimmen nicht überein. +user.error.passwordAndRepeatDoesNotMatch=Passwort und Passwortwiederholung stimmen nicht überein. user.error.usernameAlreadyExists=Benutzer:inname ist bereits vergeben. user.filter.hrPlanning=Personalplanung user.filter.privileged=Privilegierte Benutzer:innen -user.filter.restricted=Eingeschränkte Benutzer:innen +user.filter.restricted=Eingeschränkte Benutzer:innen user.filter.status=Status user.filter.syncStatus=Synchronisationsstatus user.filter.type=Typ @@ -2547,119 +2673,119 @@ user.hrPlanningEnabled=Personalplanung user.hrPlanningEnabled.not=keine Personalplanung user.hrPlanningEnabled.tooltip=Soll der oder die Benutzer:in in der Personalplanung sichtbar sein? user.jiraUsername=JIRA Benutzer:inname -user.jiraUsername.tooltip=Wird nur gebraucht, wenn dieser von dem ProjectForge-Benutzer:innamen abweicht und der oder die Benutzer:in aus ProjectForge heraus mit JIRA arbeiten möchte. +user.jiraUsername.tooltip=Wird nur gebraucht, wenn dieser von dem ProjectForge-Benutzer:innamen abweicht und der oder die Benutzer:in aus ProjectForge heraus mit JIRA arbeiten möchte. user.ldapValues=LDAP -user.list.select.title=Benutzer:in auswählen +user.list.select.title=Benutzer:in auswählen user.locale=Sprache user.localUser=Lokale:r Benutzer:in user.localUser.not=Allgemeine:r Benutzer:in user.localUser.tooltip=Lokale Benutzer:innen werden nicht mit einem externen Benutzer:innensystem synchronisiert (z. B. LDAP). user.menu.editRights=Benutzer:innenrechte user.mobilePhone=Mobilnummer -user.mobilePhone.info=Die Nummer kann als 2. Faktor benutzt werden, um z. B. das Passwort zurückzusetzen oder andere sicherheitsrelevante Bereiche zu nutzen. Die Nummer kann im Bereich Zwei-Faktor-Authentifizierung geändert werden. -user.mobilePhone.invalidFormat=Es sind nur Zahlen sowie '+' am Anfang, '-', '/' und Leerzeichen erlaubt. Die führende Ländervorwahl ist optional. Beispiel\: +49 561 316793-0 oder 0561 316793-0 -user.My2FA.expired=Für die angeforderte Aktion ist eine (erneute) Zwei-Faktor-Authentifizierung erforderlich, die nicht älter als {0} ist. +user.mobilePhone.info=Die Nummer kann als 2. Faktor benutzt werden, um z. B. das Passwort zurückzusetzen oder andere sicherheitsrelevante Bereiche zu nutzen. Die Nummer kann im Bereich Zwei-Faktor-Authentifizierung geändert werden. +user.mobilePhone.invalidFormat=Es sind nur Zahlen sowie '+' am Anfang, '-', '/' und Leerzeichen erlaubt. Die führende Ländervorwahl ist optional. Beispiel\: +49 561 316793-0 oder 0561 316793-0 +user.My2FA.expired=Für die angeforderte Aktion ist eine (erneute) Zwei-Faktor-Authentifizierung erforderlich, die nicht älter als {0} ist. user.My2FA.required=Eine (erneute) Zwei-Faktor-Authentifizierung ist erforderlich. -user.My2FA.required.extended=Eine (erneute) Zwei-Faktor-Authentifizierung ist aus Sicherheitsgründen erforderlich. Du kannst hierzu einen Code anfordern (s. Link unterhalb des Code-Feldes) oder, falls konfiguriert, deine Authenticator-App benutzen. +user.My2FA.required.extended=Eine (erneute) Zwei-Faktor-Authentifizierung ist aus Sicherheitsgründen erforderlich. Du kannst hierzu einen Code anfordern (s. Link unterhalb des Code-Feldes) oder, falls konfiguriert, deine Authenticator-App benutzen. user.My2FA.setup.authenticator.info=Smartphone einrichten\n\n\ 1. Einfach den Barcode in der Authenticator-App scannen.\n\ 2. Fertig.\n\n\ Jedesmal, wenn ProjectForge nach einem Code fragt, einfach den in der Authenticator-App eingeblendeten Code verwenden.\n\n\ -Es können mehrere Authenticator-Apps gleichzeitig (auch als Backup) genutzt werden.\n\n\ -Codes aus der Authenticator-App sind noch für ca. 30 Sekunden gültig, nachdem sie in der App erneuert wurden.\n\n\ -Diese Barcode-Ansicht ist für ca. 10 Minuten einsehbar. Anschließend wird für die erneute Anzeige eine 2FA-Authentifizierung benötigt. -user.My2FA.setup.authenticator.intro=Für die Zwei-Faktor-Authentifzierung sollte eine Authenticator-App eingerichtet werden (z. B. Microsoft-, Google-Authenticator oder Fortitoken). +Es können mehrere Authenticator-Apps gleichzeitig (auch als Backup) genutzt werden.\n\n\ +Codes aus der Authenticator-App sind noch für ca. 30 Sekunden gültig, nachdem sie in der App erneuert wurden.\n\n\ +Diese Barcode-Ansicht ist für ca. 10 Minuten einsehbar. Anschließend wird für die erneute Anzeige eine 2FA-Authentifizierung benötigt. +user.My2FA.setup.authenticator.intro=Für die Zwei-Faktor-Authentifzierung sollte eine Authenticator-App eingerichtet werden (z. B. Microsoft-, Google-Authenticator oder Fortitoken). user.My2FA.setup.authenticator.title=Authenticator-Apps -user.My2FA.setup.authenticatorKey=Authenticator-Schlüssel -user.My2FA.setup.check.fail=Code-Prüfung fehlgeschlagen. -user.My2FA.setup.check.success=Code-Prüfung erfolgreich. +user.My2FA.setup.authenticatorKey=Authenticator-Schlüssel +user.My2FA.setup.check.fail=Code-Prüfung fehlgeschlagen. +user.My2FA.setup.check.success=Code-Prüfung erfolgreich. user.My2FA.setup.disableAuthenticatorApp=Authenticator-App entfernen -user.My2FA.setup.disableAuthenticatorApp.confirmMessage=Soll die Authentificator-App nun entfernt bzw. erneuert werden? Alle bisherhigen Authentificator-Apps werden dann ungültig. -user.My2FA.setup.disableAuthenticatorApp.info=Diesen Knopf klicken, um die Authentificator-App zu entfernen oder zu erneuern. Für diese Funktion wird selber ein 2. FAktor benötigt, der nicht älter ist als 2 Minuten. +user.My2FA.setup.disableAuthenticatorApp.confirmMessage=Soll die Authentificator-App nun entfernt bzw. erneuert werden? Alle bisherhigen Authentificator-Apps werden dann ungültig. +user.My2FA.setup.disableAuthenticatorApp.info=Diesen Knopf klicken, um die Authentificator-App zu entfernen oder zu erneuern. Für diese Funktion wird selber ein 2. FAktor benötigt, der nicht älter ist als 2 Minuten. user.My2FA.setup.enableAuthenticatorApp=Authentificator-Apps einrichten -user.My2FA.setup.enableAuthenticatorApp.confirmMessage=Soll eine Authenticator-App nun eingerichtet werden? Anschließend wird für eine Anmeldung ein 2. Faktor benötigt. Diese Operation kann rückgängig gemacht werden. -user.My2FA.setup.enableAuthenticatorApp.info=Hierüber kann eine Authenticator-App eingerichtet werden. +user.My2FA.setup.enableAuthenticatorApp.confirmMessage=Soll eine Authenticator-App nun eingerichtet werden? Anschließend wird für eine Anmeldung ein 2. Faktor benötigt. Diese Operation kann rückgängig gemacht werden. +user.My2FA.setup.enableAuthenticatorApp.info=Hierüber kann eine Authenticator-App eingerichtet werden. user.My2FA.setup.info.1=### Zwei-Faktor-Authentifizierung (2FA)\n\n\ -* ProjectForge unterstützt 2FA für eine erhöhte Sicherheit. Authenticator-Apps, SMS (wenn konfiguriert) und/oder E-Mails werden als 2. Faktor unterstützt.\n\ +* ProjectForge unterstützt 2FA für eine erhöhte Sicherheit. Authenticator-Apps, SMS (wenn konfiguriert) und/oder E-Mails werden als 2. Faktor unterstützt.\n\ * Immer, wenn ProjectForge einen Code anfordert, kann ein Code aus der Authenticator-App eingegeben werden oder ein Einmalpasswort per E-Mail oder als SMS angefordert werden.\n\n\ -Wenn die Angemeldet-Bleiben-Funktion bei der Anmeldung verwendet wird, kann eine unnötig häufige Code-Abfrage vermieden werden! -user.My2FA.setup.info.2=Es wird dringend empfohlen, einen zweiten Faktor über eine Authenticator-App und/oder WebAuthn (z. B. Fidu2) und, falls verfügbar, auch eine Mobilfunknummer zum Anfordern von Codes per SMS zu konfiguieren.\n\n\ -Einige Sicherheitsfunktionen, wie z. B. ein Passwort-Reset, steht nur zur Verfügung, wenn mindestens ein 2. Faktor (Authenticator-App, SMS oder WebAuthn) konfiguriert ist. -user.My2FA.setup.info.3=Hier können 2FA-Codes getestet werden. Außerdem benötigen Änderungen auf dieser Seite eine aktuelle 2FA-Prüfung. +Wenn die Angemeldet-Bleiben-Funktion bei der Anmeldung verwendet wird, kann eine unnötig häufige Code-Abfrage vermieden werden! +user.My2FA.setup.info.2=Es wird dringend empfohlen, einen zweiten Faktor über eine Authenticator-App und/oder WebAuthn (z. B. Fidu2) und, falls verfügbar, auch eine Mobilfunknummer zum Anfordern von Codes per SMS zu konfiguieren.\n\n\ +Einige Sicherheitsfunktionen, wie z. B. ein Passwort-Reset, steht nur zur Verfügung, wenn mindestens ein 2. Faktor (Authenticator-App, SMS oder WebAuthn) konfiguriert ist. +user.My2FA.setup.info.3=Hier können 2FA-Codes getestet werden. Außerdem benötigen Änderungen auf dieser Seite eine aktuelle 2FA-Prüfung. user.My2FA.setup.info.title=Zweifaktorauthentifizierung user.My2FA.setup.showAuthenticatorKey=Authenticator-Zugang anzeigen user.My2FA.setup.sms.info=Hier kann eine Mobilfunknummer hinterlegt werden, um einen 2. Faktor per SMS anzufordern. user.My2FA.setup.sms.info.title=SMS-Konfiguration user.My2FA.setup.sms.mobileNumberRecommended=Es wird dringend empfohlen, hier die eigene Mobilfunknummer anzugeben.\n\n\ Ãœber das Mobiltelefon kann ein zweiter Faktor als SMS alternativ angefordert werden.\n\n\ -Wenn die Mobilfunknummer geändert werden soll, wird eine (neue) Zweifaktorauthentifizierung benötigt (siehe oben). +Wenn die Mobilfunknummer geändert werden soll, wird eine (neue) Zweifaktorauthentifizierung benötigt (siehe oben). user.My2FA.setup.title=Zwei-Faktor-Authentifizierung (2FA) einrichten -user.My2FACode.authentification.info=Ein zweiter Faktor ist nun erforderlich. Hier kann ein 2. Faktor (z. B. OTP-Einmalpasswort) eingegeben werden: Aus einer Authenticator-App, ein angeforderter Code per SMS oder E-Mail oder über einen registrierten WebAuthn-Token. +user.My2FACode.authentification.info=Ein zweiter Faktor ist nun erforderlich. Hier kann ein 2. Faktor (z. B. OTP-Einmalpasswort) eingegeben werden: Aus einer Authenticator-App, ein angeforderter Code per SMS oder E-Mail oder über einen registrierten WebAuthn-Token. user.My2FACode.code=Code user.My2FACode.code.info=Code aus der Authenticator-App oder der Code (Einmalpasswort), welches per E-Mail oder SMS versendet wurde. -user.My2FACode.code.validate=Prüfen -user.My2FACode.error.timePenalty.message=Die Funktion ist aus Sicherheitsgründen blockiert bis {0} ({1}) nach {2} Fehlversuchen. Bitte beachten, dass dieser Account nach {3} Fehlversuchen gesperrt wird. -user.My2FACode.error.validation=Code nicht gültig oder die Zweifaktor-Authentifizierung ist noch nicht eingerichtet. +user.My2FACode.code.validate=Prüfen +user.My2FACode.error.timePenalty.message=Die Funktion ist aus Sicherheitsgründen blockiert bis {0} ({1}) nach {2} Fehlversuchen. Bitte beachten, dass dieser Account nach {3} Fehlversuchen gesperrt wird. +user.My2FACode.error.validation=Code nicht gültig oder die Zweifaktor-Authentifizierung ist noch nicht eingerichtet. user.My2FACode.lastSuccessful2FA=Letzte erfolgreiche 2. Faktorauthentifizierung -user.My2FACode.password.info=Das Password wird nur beim Versenden des Einmalpassworts per E-Mail aus Sicherheitsgründen verlangt. -user.My2FACode.password.wrong=Passwortprüfung fehlgeschlagen. +user.My2FACode.password.info=Das Password wird nur beim Versenden des Einmalpassworts per E-Mail aus Sicherheitsgründen verlangt. +user.My2FACode.password.wrong=Passwortprüfung fehlgeschlagen. user.My2FACode.sendCode.mail=E-Mail-Code anfordern -user.My2FACode.sendCode.mail.info=Code per E-Mail anfordern. Dieser Code ist bis zu 2 Minuten gültig. +user.My2FACode.sendCode.mail.info=Code per E-Mail anfordern. Dieser Code ist bis zu 2 Minuten gültig. user.My2FACode.sendCode.mail.message=Der Code lautet: {0} user.My2FACode.sendCode.mail.sentSuccessfully=Der 2. Faktor wurde erfolgreich per E-Mail versendet um {0} user.My2FACode.sendCode.mail.title=Nachricht von ProjectForge user.My2FACode.sendCode.sms=SMS-Code anfordern -user.My2FACode.sendCode.sms.info=Coder per SMS anfordern. Dieser Code ist bis zu 2 Minuten gültig. +user.My2FACode.sendCode.sms.info=Coder per SMS anfordern. Dieser Code ist bis zu 2 Minuten gültig. user.My2FACode.sendCode.sms.sentSuccessfully=Der 2. Faktor wurde erfolgreich per SMS versendet um {0}. user.My2FACode.title=Zweifaktor-Authentifizierung -user.myAccount.teamcalwhitelist=Kalender Whitelist für Kalendersoftware +user.myAccount.teamcalwhitelist=Kalender Whitelist für Kalendersoftware user.myAccount.title.edit=Mein Zugang user.panel.error.usernameNotFound=Benutzer:inname nicht existent. user.personalPhoneIdentifiers=Telefonkennungen -user.personalPhoneIdentifiers.pleaseDefine=Anschlüsse festlegen unter 'Mein Zugang' -user.personalPhoneIdentifiers.tooltip.content=Hier können benutzerspezifische Telefon-Ids (\= interne Rufnummer) als kommaseparierte Werte angegeben werden. In der Adressenliste kann somit eine Direktwahl von diesen Telefonen durch Klicken auf die gewünschte Rufnummer initiiert werden. +user.personalPhoneIdentifiers.pleaseDefine=Anschlüsse festlegen unter 'Mein Zugang' +user.personalPhoneIdentifiers.tooltip.content=Hier können benutzerspezifische Telefon-Ids (\= interne Rufnummer) als kommaseparierte Werte angegeben werden. In der Adressenliste kann somit eine Direktwahl von diesen Telefonen durch Klicken auf die gewünschte Rufnummer initiiert werden. user.personalPhoneIdentifiers.tooltip.title=Direktwahl -user.restricted=eingeschränkt -user.restricted.not=nicht eingeschränkt -user.restrictedUser=Eingeschränkte:r Nutzer:in -user.restrictedUser.tooltip=Ein eingeschränkte:r (restricted) Nutzer:in darf sich lediglich anmelden und sein Passwort ändern. Der oder die Nutzer:in wird mit einem externen Usermanagementsystem synchronisiert. +user.restricted=eingeschränkt +user.restricted.not=nicht eingeschränkt +user.restrictedUser=Eingeschränkte:r Nutzer:in +user.restrictedUser.tooltip=Ein eingeschränkte:r (restricted) Nutzer:in darf sich lediglich anmelden und sein Passwort ändern. Der oder die Nutzer:in wird mit einem externen Usermanagementsystem synchronisiert. user.rights.title.edit=Benutzer:innenrechte bearbeiten user.sshPublicKey=SSH public key user.title.add=Neue:r Benutzer:in user.title.edit=Benutzer:in bearbeiten user.title.heading=Benutzer:in user.title.list=Benutzer:in -user.title.list.select=Benutzer:in auswählen +user.title.list.select=Benutzer:in auswählen user.unassignedGroups=Nicht assoziierte Gruppen user.username=Benutzer:inname user.users=Benutzer:in userPref.area=Bereich userPref.area.jira.project=JIRA Projekt userPref.area.jira.project.pid=Projekt ID (PID) -userPref.area.jira.project.pid.tooltip=Kann im JIRA-Dashboard z. B. aus einigen Links abgelesen werden (projectID oder pid in der referenzierten URL). Wird für die Kommunikation mit JIRA benötigt. +userPref.area.jira.project.pid.tooltip=Kann im JIRA-Dashboard z. B. aus einigen Links abgelesen werden (projectID oder pid in der referenzierten URL). Wird für die Kommunikation mit JIRA benötigt. userPref.area.kunde.favorite=Kundenfavorit userPref.area.projekt.favorite=Projektfavorit userPref.area.task.favorite=Strukturelementfavorit userPref.area.timesheet.template=Zeitberichtsvorlage userPref.area.user.favorite=Benutzerfavorit (unbenutzt) userPref.error.nameDoesAlreadyExist=Es existiert bereits ein Eintrag unter diesem Namen. -userPref.error.userIsNotOwner=Benutzer:in ist nicht Eigner:in dieser persönlichen Einstellung. +userPref.error.userIsNotOwner=Benutzer:in ist nicht Eigner:in dieser persönlichen Einstellung. userPref.favorite.create=--- anlegen --- userPref.favorite.select=--- Favoriten --- userPref.name=Name userPref.saveAsTemplate=Als Vorlage speichern userPref.template.create=--- anlegen --- userPref.template.select=--- Vorlagen --- -userPref.title.add=Neue persönliche Einstellung -userPref.title.edit=Persönliche Einstellung bearbeiten -userPref.title.heading=Persönliche Einstellungen -userPref.title.list=Liste der persönlichen Einstellungen +userPref.title.add=Neue persönliche Einstellung +userPref.title.edit=Persönliche Einstellung bearbeiten +userPref.title.heading=Persönliche Einstellungen +userPref.title.list=Liste der persönlichen Einstellungen vacation=Urlaub -vacation.annualleave=Anzahl Urlaubstage für Kalenderjahr -vacation.availabledays=Verfügbare Urlaubstage -vacation.availablevacation=Noch verfügbarer Urlaub +vacation.annualleave=Anzahl Urlaubstage für Kalenderjahr +vacation.availabledays=Verfügbare Urlaubstage +vacation.availablevacation=Noch verfügbarer Urlaub vacation.calendar=Kalender -vacation.conflict.info=Es besteht ein Konflikt mit den Abwesenheiten der Vertreter:innen. An mindestens einem Tag steht kein:e Vertreter:in zur Verfügung. Bitte andere oder weitere Vertretungen auswählen. +vacation.conflict.info=Es besteht ein Konflikt mit den Abwesenheiten der Vertreter:innen. An mindestens einem Tag steht kein:e Vertreter:in zur Verfügung. Bitte andere oder weitere Vertretungen auswählen. vacation.conflicts=Konflikte vacation.countPerDay=Urlaubsstunden pro Urlaubstag vacation.Days=Urlaubstage @@ -2683,21 +2809,21 @@ vacation.leaveOfYear=Urlaubskonto im Jahr {0} vacation.mail.action=Der Urlaubseintrag von {0} am {1} wurde {2} durch {3}. vacation.mail.action.short=Urlaubseintrag von {0} am {1} wurde {2} vacation.mail.link=Zum Urlaubseintrag -vacation.mail.modType.delete=gelöscht +vacation.mail.modType.delete=gelöscht vacation.mail.modType.insert=angelegt vacation.mail.modType.undelete=wiederhergestellt -vacation.mail.modType.update=geändert +vacation.mail.modType.update=geändert vacation.mail.period={0}-{1} ({2} Tage) -vacation.mail.reason.hr=Du erhählst diesen Urlaubseintrag von {0}, weil es ein Spezialurlaub ist, der die Freigabe von HR benötigt. -vacation.mail.reason.manager=Du wurdest zur Abstimmung ausgewählt. Bitte bearbeite diesen Urlaubseintrag in ProjectForge bzw. nimm bitte Kontakt zu {0} auf. -vacation.mail.reason.other=Du erhählst diesen Urlaubseintrag von {0} zur Information. +vacation.mail.reason.hr=Du erhählst diesen Urlaubseintrag von {0}, weil es ein Spezialurlaub ist, der die Freigabe von HR benötigt. +vacation.mail.reason.manager=Du wurdest zur Abstimmung ausgewählt. Bitte bearbeite diesen Urlaubseintrag in ProjectForge bzw. nimm bitte Kontakt zu {0} auf. +vacation.mail.reason.other=Du erhählst diesen Urlaubseintrag von {0} zur Information. vacation.mail.reason.own=Es ist dein Urlaubseintrag. -vacation.mail.reason.replacement=Du wurdest als Vertretung ausgewählt. Bitte bearbeite diesen Eintrag oder nimm Kontakt zu {0} auf. +vacation.mail.reason.replacement=Du wurdest als Vertretung ausgewählt. Bitte bearbeite diesen Eintrag oder nimm Kontakt zu {0} auf. vacation.manager=Abstimmung -vacation.neededdays=Benötigte Urlaubstage +vacation.neededdays=Benötigte Urlaubstage vacation.other=Anderer vacation.own=Eigner:in -vacation.plandannualleave=Anzahl geplanter Urlaubstage für restliches Kalenderjahr +vacation.plandannualleave=Anzahl geplanter Urlaubstage für restliches Kalenderjahr vacation.previousyearleave=Resturlaub aus dem Vorjahr vacation.previousyearleaveunused=Ungenutzer Resturlaub zum {0} vacation.previousyearleaveused=Genutzter Resturlaub aus dem Vorjahr @@ -2706,9 +2832,9 @@ vacation.remainingLeaveFromYear=Resturlaub aus dem Jahr {0} vacation.remainingLeaveFromYearUnused=Ungenutzer Resturlaub aus dem Jahr {0} vacation.replacement=Vertretung vacation.replacement.others=Weitere Vertretungen -vacation.setStartAndEndFirst= +vacation.setStartAndEndFirst= vacation.special=Sonderurlaub -vacation.special.tooltip=Sonderurlaub wird im Urlaubskonto nicht berücksichtigt. +vacation.special.tooltip=Sonderurlaub wird im Urlaubskonto nicht berücksichtigt. vacation.specialApproved=Abgestimmter Sonderurlaub vacation.specialInProgress=Sonderurlaub in Abstimmung vacation.specialVacationInYearApproved=Abgestimmter Sonderurlaub in {0} @@ -2719,12 +2845,12 @@ vacation.status=Status vacation.status.approved=Abgestimmt vacation.status.inProgress=In Abstimmung vacation.status.rejected=Abgelehnt -vacation.subscription=Abonnieren von Urlaubseinträgen -vacation.subscription.info=Urlaubseinträge können in der Kalendersicht eingesehen werden, aber auch als Kalender (s. Kalenderliste) für bestimmte Benutzer:innen oder Gruppen angelegt und auch abonniert werden. Sie können insbesondere die eigenen Urlaube und die von Kollegen oder Kolleginnen/Teams in die eigene Kalendersoftware integriert werden. +vacation.subscription=Abonnieren von Urlaubseinträgen +vacation.subscription.info=Urlaubseinträge können in der Kalendersicht eingesehen werden, aber auch als Kalender (s. Kalenderliste) für bestimmte Benutzer:innen oder Gruppen angelegt und auch abonniert werden. Sie können insbesondere die eigenen Urlaube und die von Kollegen oder Kolleginnen/Teams in die eigene Kalendersoftware integriert werden. vacation.subtotal=Zwischensumme vacation.title.add=Urlaubseintrag erstellen -vacation.title.edit=Urlaubseintrag ändern -vacation.title.heading=Urlaubseinträge +vacation.title.edit=Urlaubseintrag ändern +vacation.title.heading=Urlaubseinträge vacation.title.list=Urlaubseintragsliste vacation.usedvacation=Bereits verwendeter Urlaub vacation.vacationApproved=Abgestimmter Urlaub @@ -2733,27 +2859,27 @@ vacation.vacationInProgress=Urlaub in Abstimmung vacation.vacationInYearApproved=Abgestimmter Urlaub in {0} vacation.vacationmode=Zuordnung vacation.vacationsOfReplacements=Urlaube der Vertretungen -vacation.validate.datenotset=Das Start- und das Enddatum müssen angegeben sein. +vacation.validate.datenotset=Das Start- und das Enddatum müssen angegeben sein. vacation.validate.daysarenull=Urlaubseintrag mit 0 Arbeitstagen -vacation.validate.endbeforestart=Das gewählte Enddatum liegt vor dem Startdatum -vacation.validate.leaveapplicationexists=Für den ausgewählten Zeitraum existiert bereits ein Urlaubseintrag -vacation.validate.noCalender=Der Firmenurlaubskalender {0} muss ausgewählt werden -vacation.validate.notAllowedToSelfApprove=Sie können diesen Eintrag nicht als abgestimmt markieren. -vacation.validate.notEnoughVacationDaysLeft=Es stehen nicht mehr genügend Urlaubstage zur Verfügung -vacation.validate.startDateBeforeNow=Das gewählte Startdatum liegt vor dem aktuellen Datum. Nur HR-Mitarbeiter:innen können Daten in der Vergangenheit anlegen. -vacation.validate.usedBiggerThanPreviousYear=Die Anzahl der verbrauchten Tage aus dem Urlaub vom Vorjahr ist größer als die tatsächliche Anzahl der Urlaubstage aus dem Vorjahr. -vacation.validate.usedButNoPreviousYear=Es wurden nur die verbrauchten Tage des Urlaubs aus dem Vorjahr angegeben, aber nicht die tatsächlichen. +vacation.validate.endbeforestart=Das gewählte Enddatum liegt vor dem Startdatum +vacation.validate.leaveapplicationexists=Für den ausgewählten Zeitraum existiert bereits ein Urlaubseintrag +vacation.validate.noCalender=Der Firmenurlaubskalender {0} muss ausgewählt werden +vacation.validate.notAllowedToSelfApprove=Sie können diesen Eintrag nicht als abgestimmt markieren. +vacation.validate.notEnoughVacationDaysLeft=Es stehen nicht mehr genügend Urlaubstage zur Verfügung +vacation.validate.startDateBeforeNow=Das gewählte Startdatum liegt vor dem aktuellen Datum. Nur HR-Mitarbeiter:innen können Daten in der Vergangenheit anlegen. +vacation.validate.usedBiggerThanPreviousYear=Die Anzahl der verbrauchten Tage aus dem Urlaub vom Vorjahr ist größer als die tatsächliche Anzahl der Urlaubstage aus dem Vorjahr. +vacation.validate.usedButNoPreviousYear=Es wurden nur die verbrauchten Tage des Urlaubs aus dem Vorjahr angegeben, aber nicht die tatsächlichen. vacation.validate.vacationBeforeJoinDate=Der Urlaub darf nicht vor dem Eintrittsdatum des Mitarbeiters liegen. vacation.workingdays=Anzahl Urlaubstage webauthn.entry.displayName=Anzeigename -webauthn.entry.displayName.info=Hier kannst du einen beliebigen Namen eingeben, um deinen Token leichter zuordnen zu können. -webauthn.entry.edit=WebAuthn-Eintrag ändern -webauthn.entry.signCount=Signaturzähler -webauthn.entry.signCount.info=Zähler für Authentifizierungen (wird nach jeder Benutzung hochgezählt) +webauthn.entry.displayName.info=Hier kannst du einen beliebigen Namen eingeben, um deinen Token leichter zuordnen zu können. +webauthn.entry.edit=WebAuthn-Eintrag ändern +webauthn.entry.signCount=Signaturzähler +webauthn.entry.signCount.info=Zähler für Authentifizierungen (wird nach jeder Benutzung hochgezählt) webauthn.error.process=Fehler im Authentifizierungsprozess. -webauthn.error.userNotOwnerOrEntryDoesnotExist=Der/die Benutzer:in hat nicht das Recht, auf fremde WebAuthn-Einträge zuzugreifen oder der gesuchte Eintrag existiert nicht. +webauthn.error.userNotOwnerOrEntryDoesnotExist=Der/die Benutzer:in hat nicht das Recht, auf fremde WebAuthn-Einträge zuzugreifen oder der gesuchte Eintrag existiert nicht. webauthn.error.validate=Fehler bei der Validierung der Authentifizierungsdaten. -webauthn.info=WebAuthn ist ein moderner und sicherer Standard, um eine hardwarebasierte Zweifaktorauthentifizierung zu ermöglichen. Hier kannst du einen oder mehrere deiner Token registrieren, um eine bequeme und sichere 2FA zu nutzen. +webauthn.info=WebAuthn ist ein moderner und sicherer Standard, um eine hardwarebasierte Zweifaktorauthentifizierung zu ermöglichen. Hier kannst du einen oder mehrere deiner Token registrieren, um eine bequeme und sichere 2FA zu nutzen. webauthn.registration.2FARequired.info=Bitte de 2FA erneuern, bevor ein WebAuthn-Token registriert werden kann. webauthn.registration.button.authenticate=WebAuthn webauthn.registration.button.authenticate.info=Du kannst hier registrierte WebAuthn-Token benutzen (z. B. Yubikey). @@ -2761,21 +2887,21 @@ webauthn.registration.button.register=Registrieren webauthn.title=WebAuthn (Fido2 etc.) # (timeable) attributes -attr.deletemodal.heading=Soll dieser Eintrag wirklich gelöscht werden? -attr.deletemodal.question=Ja: Der Eintrag wird gelöscht und alle Änderungen auf dieser Seite werden gespeichert.
Abbrechen: Der Eintrag wird nicht gelöscht und Sie bleiben auf dieser Seite. +attr.deletemodal.heading=Soll dieser Eintrag wirklich gelöscht werden? +attr.deletemodal.question=Ja: Der Eintrag wird gelöscht und alle Änderungen auf dieser Seite werden gespeichert.
Abbrechen: Der Eintrag wird nicht gelöscht und Sie bleiben auf dieser Seite. attr.instantOfTime=Zeitpunkt -attr.savemodal.heading=Soll die Änderungen gespeichert werden? -attr.savemodal.question=Ja: Alle Änderungen auf dieser Seite werden gespeichert.
Abbrechen: Die Änderungen werden nicht gespeichert aber auch nicht verworfen und Sie bleiben auf dieser Seite. +attr.savemodal.heading=Soll die Änderungen gespeichert werden? +attr.savemodal.question=Ja: Alle Änderungen auf dieser Seite werden gespeichert.
Abbrechen: Die Änderungen werden nicht gespeichert aber auch nicht verworfen und Sie bleiben auf dieser Seite. attr.starttime.alreadyexists.day=Es existiert bereits ein Eintrag mit gleichem Datum (Tag). attr.starttime.alreadyexists.month=Es existiert bereits ein Eintrag mit gleichem Datum (Monat). -attr.validFrom=Gültig ab +attr.validFrom=Gültig ab birthdayButler.email.content=Im Monat {0} haben {1} Mitarbeiter Geburtstag. Im Anhang findest du die Liste der Geburtstage. -birthdayButler.email.content.noBirthdaysFound=Im Monat {0} wurden keine Geburtstagseinträge gefunden. +birthdayButler.email.content.noBirthdaysFound=Im Monat {0} wurden keine Geburtstagseinträge gefunden. birthdayButler.email.opening=Ein neuer Monat, eine neue Geburtstagsliste! -birthdayButler.email.subject=Geburtstagsliste für -birthdayButler.month.response.noEntry=Es gibt keine Geburtstagseinträge für diesen Monat. -birthdayButler.month.response.nothingSelected=Der Monat muss ausgewählt sein. +birthdayButler.email.subject=Geburtstagsliste für +birthdayButler.month.response.noEntry=Es gibt keine Geburtstagseinträge für diesen Monat. +birthdayButler.month.response.nothingSelected=Der Monat muss ausgewählt sein. birthdayButler.organization.noMatchingUser=Es gibt keine Benutzer:innen, die der in projectforge.properties gesetzten Organisation zugeordnet sind. birthdayButler.organization.notSet=Es ist keine Organisation in den application.properties definiert. -> projectforge.birthdayTool.organization=Your Organization birthdayButler.wordDocument.error=Es ist ein Fehler beim Erstellen des Word-Dokuments aufgetreten. diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt index 6baebc75ee..2bff5443d4 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt @@ -287,13 +287,15 @@ class AddressViewPageRest : AbstractDynamicPageRest() { if (email.isNullOrBlank()) { return } + val emailObject = EMail(email) col.add( UIRow() .add(UICol(6).add(UILabel(title))) - .add(UICol(6).add(UICustomized("email", mutableMapOf("data" to EMail(email))))) + .add(UICol(6).add(UICustomized("email", mutableMapOf("email" to emailObject)))) ) } + private fun createAddressCol( row: UIRow, numberOfAddresses: Int, diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/dto/User.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/dto/User.kt index 5b073ab174..7d426012eb 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/dto/User.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/dto/User.kt @@ -238,6 +238,10 @@ class User( users?.forEach { it.displayName = userService.getUser(it.id)?.displayName } } + fun restoreEmails(users: List?, userService: UserService) { + users?.forEach { it.email = userService.getUser(it.id)?.email } + } + /** * Converts csv of user ids to list of user. */ diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/fibu/CustomerPagesRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/fibu/CustomerPagesRest.kt index b4ff3534ae..bbccaf7806 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/fibu/CustomerPagesRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/fibu/CustomerPagesRest.kt @@ -31,7 +31,6 @@ import org.projectforge.rest.config.JacksonConfiguration import org.projectforge.rest.config.Rest import org.projectforge.rest.core.AbstractDTOPagesRest import org.projectforge.rest.dto.Customer -import org.projectforge.rest.dto.Konto import org.projectforge.ui.* import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollCronJobs.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollCronJobs.kt index ba888290a3..d4689e210d 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollCronJobs.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollCronJobs.kt @@ -26,8 +26,10 @@ package org.projectforge.rest.poll import org.projectforge.business.poll.PollDO import org.projectforge.business.poll.PollDao import org.projectforge.business.poll.PollResponseDao +import org.projectforge.business.user.service.UserService import org.projectforge.framework.i18n.translateMsg import org.projectforge.mail.MailAttachment +import org.projectforge.rest.dto.PostData import org.projectforge.rest.poll.excel.ExcelExport import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -54,6 +56,9 @@ class PollCronJobs { @Autowired private lateinit var exporter: ExcelExport + @Autowired + private lateinit var userService: UserService + private val log: Logger = LoggerFactory.getLogger(PollCronJobs::class.java) /** @@ -94,12 +99,12 @@ class PollCronJobs { return excel } } - // add all attendees mails + val owner = userService.getUser(poll.owner?.id) val mailTo: ArrayList = ArrayList(poll.attendees?.map { it.email }?.mapNotNull { it } ?: emptyList()) val mailFrom = pollDO.owner?.email.toString() - val mailSubject = translateMsg("poll.mail.ended.subject") - val mailContent = translateMsg("poll.mail.ended.content", pollDO.title, pollDO.owner?.displayName) + val mailSubject = translateMsg("poll.mail.endedafterdeadline.subject", poll.title) + val mailContent = translateMsg("poll.mail.endedafterdeadline.content", pollDO.title, owner?.displayName, ) pollDao.internalSaveOrUpdate(pollDO) log.info("Set state of poll (${pollDO.id}) ${pollDO.title} to FINISHED") @@ -153,4 +158,4 @@ class PollCronJobs { pollDao.internalMarkAsDeleted(poll) } } -} +} \ No newline at end of file diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollInfoPageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollInfoPageRest.kt index 0a05b79f55..e5e58afd71 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollInfoPageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollInfoPageRest.kt @@ -79,36 +79,36 @@ class PollInfoPageRest : AbstractDynamicPageRest() { layout.add(field) layout.add( - UIFieldset().add(UILabel("Single Response " + translate("poll.question"))).add( + UIFieldset().add(UILabel(translate("poll.question.singletitel"))).add( UICol() .add( UIReadOnlyField( "question", - label = "poll.question", + label = "poll.question.single", value = translate("poll.manual.singleResponse") ) ) ) ) layout.add( - UIFieldset().add(UILabel("Multiple Response " + translate("poll.question"))).add( + UIFieldset().add(UILabel(translate("poll.question.multititle"))).add( UICol() .add( UIReadOnlyField( "question", - label = "poll.question", + label = "poll.question.multi", value = translate("poll.manual.multiResponse") ) ) ) ) layout.add( - UIFieldset().add(UILabel("Text " + translate("poll.question"))).add( + UIFieldset().add(UILabel(translate("poll.question.texttitle"))).add( UICol() .add( UIReadOnlyField( "question", - label = "poll.question", + label = "poll.question.text", value = translate("poll.manual.textQuestion") ) ) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt index 705f8dc813..44c998c444 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt @@ -71,7 +71,6 @@ class PollMailService { } catch (e: Exception) { log.error(e.message, e) } - } fun getAllMails(poll: Poll): List { @@ -81,24 +80,35 @@ class PollMailService { val accessUserIds = UserService().getUserIds(groupService.getGroupUsers(accessGroupIds)) val accessUsers = User.toUserList(accessUserIds) + var userList = fullAccessUser accessUsers?.forEach { user -> if (fullAccessUser.none { it.id == user.id }) { - fullAccessUser.add(user) + userList.add(user) } } var owner = User.getUser(poll.owner?.id, false) if (owner != null) { - fullAccessUser.add(owner) + userList.add(owner) } attendees?.forEach { if (!fullAccessUser.contains(it)) { - fullAccessUser.add(it) + userList.add(it) } } - User.restoreDisplayNames(fullAccessUser, userService) - return fullAccessUser.mapNotNull { it.email } + User.restoreDisplayNames(userList, userService) + User.restoreEmails(userList, userService) + return userList.mapNotNull { it.email } } + fun getAllFullAccessEmails(poll: Poll): List { + val fullAccessUser = poll.fullAccessUsers?.toMutableList() ?: mutableListOf() + val accessGroupIds = poll.fullAccessGroups?.filter { it.id != null }?.map { it.id!! }?.toIntArray() + + var userList = fullAccessUser + + User.restoreEmails(userList, userService) + return userList.mapNotNull { it.email } + } } diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt index dee4844a7c..7ce8a1d70d 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt @@ -67,6 +67,8 @@ import javax.servlet.http.HttpServletRequest @RequestMapping("${Rest.URL}/poll") class PollPageRest : AbstractDTOPagesRest(PollDao::class.java, "poll.title") { + private var emailSent: Boolean = false + private val log: Logger = LoggerFactory.getLogger(PollPageRest::class.java) @Autowired @@ -221,7 +223,8 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. .add( UISelect( "questionType", - values = BaseType.values().map { UISelectValue(it, it.name) }, + values = BaseType.values() + .map { UISelectValue(it, translateMsg("poll.questionType." + it.name)) }, label = "poll.questionType" ) ) @@ -250,6 +253,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. .add( UICol(UILength(xs = 3, sm = 3, md = 3, lg = 3)) .add( + //Sollte zu UISelect geändert werden um mehrere Vorlagen erstellen zu können ohne mehrer Buttons zu haben UIButton.createDefaultButton( id = "template-button", responseAction = ResponseAction( @@ -298,12 +302,26 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. @PutMapping("/finish") fun changeStateToFinish( - request: HttpServletRequest, + request: HttpServletRequest, obj: PollDO, @RequestBody postData: PostData ): ResponseEntity { postData.data.state = PollDO.State.FINISHED postData.data.deadline = LocalDate.now() - return super.saveOrUpdate(request, postData) + val responseEntity = super.saveOrUpdate(request, postData) + + if (postData.data.state == PollDO.State.FINISHED) { + + val owner = userService.getUser(postData.data.owner?.id) + val mailFrom = owner?.email.toString() + val mailTo = pollMailService.getAllMails(postData.data) + val mailSubject = translateMsg("poll.mail.ended.subject", postData.data.title) + val mailContent = translateMsg("poll.mail.ended.content", postData.data.title, owner?.displayName) + pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) + emailSent = true + + } + + return responseEntity } @@ -317,26 +335,39 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. override fun onAfterSaveOrUpdate(request: HttpServletRequest, obj: PollDO, postData: PostData) { - // add all attendees mails - var mailTo = pollMailService.getAllMails(postData.data) - - val owner = userService.getUser(obj.owner?.id) - val mailFrom = owner?.email.toString() - val mailSubject: String - val mailContent: String - - if (postData.data.isAlreadyCreated()) { - mailSubject = translateMsg("poll.mail.update.subject") - mailContent = translateMsg( - "poll.mail.update.content", obj.title, owner?.displayName - ) - } else { - mailSubject = translateMsg("poll.mail.created.subject") - mailContent = translateMsg( - "poll.mail.created.content", obj.title, owner?.displayName - ) + if (postData.data.state != PollDO.State.FINISHED) { + + var mailTo = pollMailService.getAllMails(postData.data) + + val owner = userService.getUser(obj.owner?.id) + val mailFrom = owner?.email.toString() + val mailSubject: String + val mailContent: String + + if (postData.data.isAlreadyCreated()) { + + var mailTo = pollMailService.getAllFullAccessEmails(postData.data) + + mailSubject = translateMsg("poll.mail.update.subject") + mailContent = translateMsg("poll.mail.update.content", obj.title, owner?.displayName + ) + + pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) + + + } else { + mailSubject = translateMsg("poll.mail.created.subject", obj.title) + mailContent = translateMsg( + "poll.mail.created.content", + obj.title, + owner?.displayName, + "http://localhost:8080/react/pollResponse/dynamic/?pollId=${obj.id}" + ) + + pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) + } + } - pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) super.onAfterSaveOrUpdate(request, obj, postData) } @@ -367,10 +398,10 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. ): ResponseEntity { val dto = postData.data - val type = dto.questionType?.let { BaseType.valueOf(it) } ?: BaseType.TextQuestion + val type = dto.questionType?.let { BaseType.valueOf(it) } ?: BaseType.TextQuestion; val question = Question(uid = UUID.randomUUID().toString(), type = type) if (type == BaseType.SingleResponseQuestion) { - question.answers = mutableListOf("yes", "no") + question.answers = mutableListOf("", "") } dto.inputFields?.add(question) @@ -613,7 +644,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. dataType: UIDataType = UIDataType.STRING ): UIElement { return if (isReadOnly) - UIReadOnlyField(id, label = label, dataType = dataType) + UIInput(id, label = label, dataType = dataType) else UIInput(id, label = label, dataType = dataType) } diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt index 55dc3f8f4d..c7bf53815f 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt @@ -183,7 +183,7 @@ class PollResponsePageRest : AbstractDynamicPageRest() { PollPageRest.getUiElement( pollDto.isFinished(), "responses[$index].answers[0]", - "poll.question.textQuestion", + "poll.question.TextQuestion", UIDataType.STRING ) ) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PremadeQuestions.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PremadeQuestions.kt index b36117c030..987d2b836c 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PremadeQuestions.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PremadeQuestions.kt @@ -47,7 +47,7 @@ val PREMADE_QUESTIONS = mapOf( ), "CAN_HAVE_CHILDREN" to Question( uid = UUID.randomUUID().toString(), - question = "Nimmst du ein Kind mit? (Name der Begleitung)", + question = "Nimmst du ein Kind mit? (Name des Kindes)", type = BaseType.TextQuestion, answers = mutableListOf("") ), diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/Question.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/Question.kt index 006d3188a4..f69e487753 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/Question.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/Question.kt @@ -47,3 +47,4 @@ class Question( enum class BaseType { TextQuestion, SingleResponseQuestion, MultiResponseQuestion, } + From c2144ae16471616a6b5d67386365106c2e2d2543 Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Tue, 12 Mar 2024 09:36:04 +0100 Subject: [PATCH 02/16] Excel Export with Email, Excel Fix, Attende Condition, PremadeQuestion Dropdown, --- .../org/projectforge/business/poll/PollDO.kt | 2 + .../main/resources/I18nResources.properties | 4 + .../resources/I18nResources_de.properties | 26 +- .../kotlin/org/projectforge/rest/poll/Poll.kt | 1 + .../projectforge/rest/poll/PollMailService.kt | 18 ++ .../projectforge/rest/poll/PollPageRest.kt | 235 ++++++++++++------ .../rest/poll/PollResponsePageRest.kt | 30 ++- .../projectforge/rest/poll/types/PQuestion.kt | 25 ++ .../rest/poll/types/PremadeQuestions.kt | 63 ++++- .../projectforge/rest/poll/types/Question.kt | 2 +- 10 files changed, 312 insertions(+), 94 deletions(-) create mode 100644 projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PQuestion.kt diff --git a/projectforge-business/src/main/kotlin/org/projectforge/business/poll/PollDO.kt b/projectforge-business/src/main/kotlin/org/projectforge/business/poll/PollDO.kt index 3dd2559ca4..cf1e60e418 100644 --- a/projectforge-business/src/main/kotlin/org/projectforge/business/poll/PollDO.kt +++ b/projectforge-business/src/main/kotlin/org/projectforge/business/poll/PollDO.kt @@ -28,6 +28,8 @@ import org.projectforge.business.poll.filter.PollAssignment import org.projectforge.business.poll.filter.PollState import org.projectforge.common.StringHelper import org.projectforge.common.anots.PropertyInfo +import org.projectforge.framework.i18n.translate +import org.projectforge.framework.i18n.translateMsg import org.projectforge.framework.persistence.api.AUserRightId import org.projectforge.framework.persistence.entities.DefaultBaseDO import org.projectforge.framework.persistence.user.api.ThreadLocalUserContext diff --git a/projectforge-business/src/main/resources/I18nResources.properties b/projectforge-business/src/main/resources/I18nResources.properties index d52c25cf05..e328ad239d 100644 --- a/projectforge-business/src/main/resources/I18nResources.properties +++ b/projectforge-business/src/main/resources/I18nResources.properties @@ -2065,6 +2065,10 @@ poll.question.text=Question poll.question.single=Question poll.question.multi=Question +poll.question.TextQuestion=Answer +poll.question.SingleResponseQuestion=Question +poll.question.MultiResponseQuestion=Question + diff --git a/projectforge-business/src/main/resources/I18nResources_de.properties b/projectforge-business/src/main/resources/I18nResources_de.properties index 4ce0303f60..10afc1604c 100644 --- a/projectforge-business/src/main/resources/I18nResources_de.properties +++ b/projectforge-business/src/main/resources/I18nResources_de.properties @@ -174,7 +174,7 @@ redraw=Neu zeichnen refresh=Neu laden reload=Neu laden rename=Umbenennen -reset=Rücksetzen +reset=Zurücksetzten save=Speichern search=Suchen select=Auswählen @@ -195,6 +195,10 @@ upload=Hochladen uptodate=aktuell wizard=Assistent + +State.running=Laufend +poll.state.running +running=Laufend # Common akquise=Akquise changes=Änderungen @@ -2113,6 +2117,11 @@ poll.location=Ort location=Ort owner=Ersteller +poll.premade.template=Vorlage hinzufügen +poll.questionTemplate=Vorlagen +poll.templateTypeNeujahrsfeier=Neujahrsfeier +poll.templateTypeSommerfest=Sommerfest +poll.templateTypeTeamessen=Teamessen #Umfrage wurde erstellt @@ -2159,6 +2168,11 @@ poll.mail.ended.content=

Liebe Teilnehmerinnen und Teilnehmer,

\

Mit Freundlichen Grüßen,

\

{1}

+poll.mail.ended.fullAccess.subject=Die Umfrage mit dem Title "{0}" wurde Beendet +poll.mail.ended.fullAccess.content=

Den Text nochmal ändern damit die Full Access\ +\User bescheid wissen das diese Email nur die Excel enthält

+ + #Umfrage abgelaufen poll.mail.endedafterdeadline.subject=Die Umfrage mit dem Title "{0}" ist Beendet die Deadline ist abgelaufen poll.mail.endedafterdeadline.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ @@ -2170,12 +2184,11 @@ poll.mail.endedafterdeadline.content=

Liebe Teilnehmerinnen und Teilnehmer,

{1}

- -poll.manual.multiResponse=Eine Frage, die bei der man eine Antwort auswählen kann. Gut geeignet für eine Datumsumfrage für Verfügbarkeiten. -poll.manual.questions=Anschließend werden die Fragen der Umfrage angelegt. Die Fragen können aus verschiedenen Typen bestehen. -poll.manual.singleResponse=Eine Frage, die bei der man eine Antwort auswählen kann. Beispielsweise für eine simple Ja- oder Nein-Frage. -poll.manual.textQuestion=Eine Frage, die bei der man mit Freitext antworten kann. Gut geeignet für formloses Feedback. poll.manual.title=Anleitung, um eine Umfrage zu erstellen | Als Erstes muss man den +poll.manual.questions=Anlegen. Anschließend werden die Fragen der Umfrage angelegt. Die Fragen können aus verschiedenen Typen bestehen. +poll.manual.singleResponse=Eine Frage, bei der man eine Antwort auswählen kann. Beispielsweise für eine simple Ja- oder Nein-Frage. +poll.manual.multiResponse=Eine Frage, bei der man mehrere Antworten auswählen kann. Gut geeignet für eine Terminabfragen oder Essenabfrage. +poll.manual.textQuestion=Eine Frage, bei der man mit Freitext antworten kann. Gut geeignet für formloses Feedback. poll.other=Andere poll.owner=Ersteller poll.popup.closed=Umfrage wurde bereits beendet @@ -2215,6 +2228,7 @@ poll.questionType=Fragetypen poll.question.TextQuestion=Antwort poll.question.SingleResponseQuestion=Frage poll.question.MultiResponseQuestion=Frage +poll.question.AnnotationField=Anmerkungen: #Fragen Container Überschrift TextQuestion=Frage mit freier text Antwort diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/Poll.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/Poll.kt index f14ac0196f..d5566b51e8 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/Poll.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/Poll.kt @@ -40,6 +40,7 @@ class Poll( var deadline: LocalDate? = null, var state: PollDO.State? = PollDO.State.RUNNING, var questionType: String? = null, + var prequestionType: String? = null, var inputFields: MutableList? = mutableListOf(), var fullAccessGroups: List? = null, var fullAccessUsers: List? = null, diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt index 44c998c444..8038cb993d 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt @@ -24,6 +24,9 @@ package org.projectforge.rest.poll import org.projectforge.business.group.service.GroupService +import org.projectforge.business.poll.PollDO +import org.projectforge.business.poll.PollDao +import org.projectforge.business.poll.PollResponseDao import org.projectforge.business.user.service.UserService import org.projectforge.mail.Mail import org.projectforge.mail.MailAttachment @@ -46,6 +49,12 @@ class PollMailService { @Autowired private lateinit var userService: UserService + @Autowired + private lateinit var pollDao: PollDao + + @Autowired + private lateinit var pollResponseDao: PollResponseDao + private val log: Logger = LoggerFactory.getLogger(PollMailService::class.java) fun sendMail( @@ -111,4 +120,13 @@ class PollMailService { User.restoreEmails(userList, userService) return userList.mapNotNull { it.email } } + + fun getAllAttendesEmails(poll: Poll): List { + val attendees = poll.attendees + + var userList = attendees + + User.restoreEmails(userList, userService) + return userList!!.mapNotNull { it.email } + } } diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt index 7ce8a1d70d..38de4a0bea 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt @@ -40,16 +40,19 @@ import org.projectforge.framework.persistence.api.MagicFilter import org.projectforge.framework.persistence.api.QueryFilter import org.projectforge.framework.persistence.api.impl.CustomResultFilter import org.projectforge.framework.persistence.user.api.ThreadLocalUserContext +import org.projectforge.mail.MailAttachment import org.projectforge.menu.MenuItem import org.projectforge.menu.MenuItemTargetType import org.projectforge.rest.config.Rest import org.projectforge.rest.config.RestUtils -import org.projectforge.rest.core.* -import org.projectforge.rest.dto.* +import org.projectforge.rest.core.AbstractDTOPagesRest +import org.projectforge.rest.core.PagesResolver +import org.projectforge.rest.core.RestResolver +import org.projectforge.rest.dto.Group +import org.projectforge.rest.dto.PostData +import org.projectforge.rest.dto.User import org.projectforge.rest.poll.excel.ExcelExport -import org.projectforge.rest.poll.types.BaseType -import org.projectforge.rest.poll.types.PREMADE_QUESTIONS -import org.projectforge.rest.poll.types.Question +import org.projectforge.rest.poll.types.* import org.projectforge.ui.* import org.projectforge.ui.filter.UIFilterListElement import org.slf4j.Logger @@ -63,12 +66,11 @@ import java.time.LocalDateTime import java.util.* import javax.servlet.http.HttpServletRequest + @RestController @RequestMapping("${Rest.URL}/poll") class PollPageRest : AbstractDTOPagesRest(PollDao::class.java, "poll.title") { - private var emailSent: Boolean = false - private val log: Logger = LoggerFactory.getLogger(PollPageRest::class.java) @Autowired @@ -86,6 +88,9 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. @Autowired private lateinit var excelExport: ExcelExport + @Autowired + private lateinit var exporter: ExcelExport + @Autowired private lateinit var pollResponseDao: PollResponseDao @@ -215,64 +220,89 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. .add(UISelect.createGroupSelect(lc, "fullAccessGroups", true, "poll.fullAccessGroups")) .add(UISelect.createUserSelect(lc, "attendees", true, "poll.attendees")) .add(UISelect.createGroupSelect(lc, "groupAttendees", true, "poll.groupAttendees")) - if (!dto.isAlreadyCreated()) { + + if(!dto.isAlreadyCreated()) { + fieldset.add( UIRow() .add( - UICol(UILength(xs = 9, sm = 9, md = 9, lg = 9)) + UICol(UILength(xs = 10, sm = 10, md = 10, lg = 10)) .add( UISelect( - "questionType", - values = BaseType.values() - .map { UISelectValue(it, translateMsg("poll.questionType." + it.name)) }, - label = "poll.questionType" - ) - ) - ) - .add( - UICol(UILength(xs = 3, sm = 3, md = 3, lg = 3)) - .add(UISpacer()) - .add( - UIButton.createDefaultButton( - id = "add-question-button", - title = "poll.button.addQuestion", - responseAction = ResponseAction( - "${Rest.URL}/poll/add", - targetType = TargetType.PUT - ), - default = false + "prequestionType", + values = PreType.values() + .map { UISelectValue(it, translateMsg("poll.templateType" + it.name)) }, + label = "poll.questionTemplate" ) ) ) ) + .add( UIRow() .add( - UICol(UILength(xs = 9, sm = 9, md = 9, lg = 9)) + UICol(UILength(xs = 11, sm = 11, md = 11, lg = 11)) ) .add( - UICol(UILength(xs = 3, sm = 3, md = 3, lg = 3)) + UICol(UILength(xs = 2, sm = 2, md = 2, lg = 2)) .add( - //Sollte zu UISelect geändert werden um mehrere Vorlagen erstellen zu können ohne mehrer Buttons zu haben UIButton.createDefaultButton( id = "template-button", + title = "poll.premade.template", responseAction = ResponseAction( "${Rest.URL}/poll/addPremadeQuestions", targetType = TargetType.PUT ), - title = "poll.button.template", default = false ) ) ) ) + + fieldset.add( + UIRow() + .add( + UICol(UILength(xs = 10, sm = 10, md = 10, lg = 10)) + .add( + UISelect( + "questionType", + values = BaseType.values() + .map { UISelectValue(it, translateMsg("poll.questionType." + it.name)) }, + label = "poll.questionType" + ) + ) + ) + ) + + + + .add( + UIRow() + .add( + UICol(UILength(xs = 11, sm = 11, md = 11, lg = 11)) + ) + .add( + UICol(UILength(xs = 3, sm = 3, md = 3, lg = 3)) + .add(UISpacer()) + .add( + UIButton.createDefaultButton( + id = "add-question-button", + title = "poll.button.addQuestion", + responseAction = ResponseAction( + "${Rest.URL}/poll/add", + targetType = TargetType.PUT + ), + default = false + ) + ) + ) + ) } addQuestionFieldset(layout, dto, fieldset) layout.watchFields.add("delegationUser") layout.watchFields.addAll(listOf("groupAttendees")) - val processedLayout = LayoutUtils.processEditPage(layout, dto, this) if (!dto.isFinished()) { processedLayout.actions.filterIsInstance().find { @@ -300,45 +330,97 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. } + @GetMapping("export") + fun export(@RequestParam("id") id: String): ResponseEntity? { + val poll = Poll() + val pollDo = pollDao.getById(id.toInt()) + poll.copyFrom(pollDo) + User.restoreDisplayNames(poll.attendees, userService) + val bytes: ByteArray? = excelExport + .getExcel(poll) + val filename = ("${poll.title}_${LocalDateTime.now().year}_Result.xlsx") + + if (bytes == null || bytes.isEmpty()) { + log.error("Oops, xlsx is empty. Filename: $filename") + return null + } + log.info("Exporting $filename") + return RestUtils.downloadFile(filename, bytes) + } + @PutMapping("/finish") fun changeStateToFinish( request: HttpServletRequest, obj: PollDO, - @RequestBody postData: PostData + @RequestBody postData: PostData, ): ResponseEntity { postData.data.state = PollDO.State.FINISHED postData.data.deadline = LocalDate.now() val responseEntity = super.saveOrUpdate(request, postData) + var emailSent: Boolean = false + var FUemailSent: Boolean = false - if (postData.data.state == PollDO.State.FINISHED) { + if (postData.data.state == PollDO.State.FINISHED) { - val owner = userService.getUser(postData.data.owner?.id) - val mailFrom = owner?.email.toString() - val mailTo = pollMailService.getAllMails(postData.data) - val mailSubject = translateMsg("poll.mail.ended.subject", postData.data.title) - val mailContent = translateMsg("poll.mail.ended.content", postData.data.title, owner?.displayName) - pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) - emailSent = true - } + val poll = Poll() + val id: String + id = postData.data.id.toString() + val pollDo = pollDao.getById(id.toInt()) + poll.copyFrom(pollDo) + User.restoreDisplayNames(poll.attendees, userService) + + val excel = excelExport.getExcel(poll) + val filename = ("${postData.data.title}_${LocalDateTime.now().year}_Result.xlsx") + + val mailAttachment = object : MailAttachment { + override fun getFilename(): String { + return filename + } + + override fun getContent(): ByteArray? { + return excel + } + } + + if (FUemailSent != true) { + + val owner = userService.getUser(postData.data.owner?.id) + val mailFrom = owner?.email.toString() + val mailTo = pollMailService.getAllFullAccessEmails(postData.data) + val mailSubject = translateMsg("poll.mail.ended.fullAccess.subject", postData.data.title) + val mailContent = translateMsg("poll.mail.ended.fullAccess.content", postData.data.title, owner?.displayName) + pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent, listOf(mailAttachment)) + FUemailSent = true + } + + val owner = userService.getUser(postData.data.owner?.id) + val mailFrom = owner?.email.toString() + val mailTo = pollMailService.getAllAttendesEmails(postData.data) + val mailSubject = translateMsg("poll.mail.ended.subject", postData.data.title) + val mailContent = translateMsg("poll.mail.ended.content", postData.data.title, owner?.displayName) + pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) + emailSent = true + } return responseEntity } - override fun onBeforeSaveOrUpdate(request: HttpServletRequest, obj: PollDO, postData: PostData) { if (obj.inputFields.isNullOrEmpty() || obj.inputFields.equals("[]")) { throw AccessException("poll.error.oneQuestionRequired") } - super.onBeforeSaveOrUpdate(request, obj, postData) + if (obj.attendeeIds.isNullOrEmpty() || obj.attendeeIds.equals("[]")) { + throw AccessException("poll.error.oneAttendeRequired") + } + + super.onBeforeSaveOrUpdate(request, obj,postData) } override fun onAfterSaveOrUpdate(request: HttpServletRequest, obj: PollDO, postData: PostData) { if (postData.data.state != PollDO.State.FINISHED) { - var mailTo = pollMailService.getAllMails(postData.data) - val owner = userService.getUser(obj.owner?.id) val mailFrom = owner?.email.toString() val mailSubject: String @@ -346,16 +428,20 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. if (postData.data.isAlreadyCreated()) { - var mailTo = pollMailService.getAllFullAccessEmails(postData.data) + val mailTo = pollMailService.getAllFullAccessEmails(postData.data) mailSubject = translateMsg("poll.mail.update.subject") - mailContent = translateMsg("poll.mail.update.content", obj.title, owner?.displayName + mailContent = translateMsg( + "poll.mail.update.content", obj.title, owner?.displayName ) pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) } else { + + val mailTo = pollMailService.getAllMails(postData.data) + mailSubject = translateMsg("poll.mail.created.subject", obj.title) mailContent = translateMsg( "poll.mail.created.content", @@ -366,9 +452,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) } - } - super.onAfterSaveOrUpdate(request, obj, postData) } @@ -402,6 +486,8 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. val question = Question(uid = UUID.randomUUID().toString(), type = type) if (type == BaseType.SingleResponseQuestion) { question.answers = mutableListOf("", "") + } else if (type == BaseType.MultiResponseQuestion) { + question.answers = mutableListOf("", "", "") } dto.inputFields?.add(question) @@ -452,12 +538,24 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. @PutMapping("/addPremadeQuestions") private fun addPremadeQuestionsField( - @RequestBody postData: PostData, + @RequestBody postData: PostData ): ResponseEntity { val dto = postData.data - PREMADE_QUESTIONS.entries.forEach { entry -> - dto.inputFields?.add(entry.value) + val ptype = dto.prequestionType?.let { PreType.valueOf(it) } ?: PreType.Neujahrsfeier + val question = PQuestion(uid = UUID.randomUUID().toString(), pType = ptype) + if (ptype == PreType.Sommerfest) { + Sommerfest.entries.forEach { entry -> + dto.inputFields?.add(entry.value) + } + } else if (ptype == PreType.Neujahrsfeier) { + Neujahrsfeier.entries.forEach { entry -> + dto.inputFields?.add(entry.value) + } + } else if (ptype == PreType.Teamessen) { + Teamessen.entries.forEach { entry -> + dto.inputFields?.add(entry.value) + } } return ResponseEntity.ok( @@ -466,7 +564,6 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. ) } - private fun addQuestionFieldset(layout: UILayout, dto: Poll, fieldset: UIFieldset) { fieldset.add(UISpacer()) dto.inputFields?.forEachIndexed { index, field -> @@ -515,7 +612,6 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. } } - private fun generateSingleAndMultiResponseAnswer( objGiven: Boolean, inputFieldIndex: Int, @@ -613,25 +709,6 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. } - @GetMapping("export") - fun export(@RequestParam("id") id: String): ResponseEntity? { - val poll = Poll() - val pollDo = pollDao.getById(id.toInt()) - poll.copyFrom(pollDo) - User.restoreDisplayNames(poll.attendees, userService) - val bytes: ByteArray? = excelExport - .getExcel(poll) - val filename = ("${poll.title}_${LocalDateTime.now().year}_Result.xlsx") - - if (bytes == null || bytes.isEmpty()) { - log.error("Oops, xlsx is empty. Filename: $filename") - return null - } - log.info("Exporting $filename") - return RestUtils.downloadFile(filename, bytes) - } - - companion object { /** * Once created, questions should be ReadOnly @@ -644,7 +721,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. dataType: UIDataType = UIDataType.STRING ): UIElement { return if (isReadOnly) - UIInput(id, label = label, dataType = dataType) + UIReadOnlyField(id, label = label, dataType = dataType) else UIInput(id, label = label, dataType = dataType) } @@ -677,6 +754,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. /** * restricts the user access accordingly */ + private fun getUserAccess(pollDto: Poll): UILayout.UserAccess { val pollDO = PollDO() pollDto.copyTo(pollDO) @@ -698,5 +776,8 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. } } } - } + + + + diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt index c7bf53815f..2cb797df19 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt @@ -126,8 +126,7 @@ class PollResponsePageRest : AbstractDynamicPageRest() { .add(UIReadOnlyField(value = pollDto.location, label = translateMsg("poll.location"))) .add(UIReadOnlyField(value = pollDto.owner?.displayName, label = translateMsg("poll.owner"))) .add(UIReadOnlyField(value = pollDto.deadline.toString(), label = translateMsg("poll.deadline"))) - .add(UISpacer()) - .add(UISpacer()) + if (!pollDto.isFinished() && ThreadLocalUserContext.userId === questionOwnerId) { val fieldSetDelegationUser = UIFieldset(title = "poll.userDelegation") @@ -165,6 +164,7 @@ class PollResponsePageRest : AbstractDynamicPageRest() { pollResponse.copyFrom(it) } + var index = 0 pollDto.inputFields?.forEachIndexed { index, field -> val fieldSetQuestions = UIFieldset(title = field.question) val questionAnswer = QuestionAnswer() @@ -194,24 +194,36 @@ class PollResponsePageRest : AbstractDynamicPageRest() { if (pollResponse.responses?.get(index)?.answers?.getOrNull(index2) == null) { pollResponse.responses?.get(index)?.answers?.add(index2, false) } - if (field.type == BaseType.MultiResponseQuestion) { + if (field.type == BaseType.SingleResponseQuestion) { col.add( - UICheckbox( + UIRadioButton( "responses[$index].answers[$index2]", + value = field.answers?.get(index2) ?: "", label = field.answers?.get(index2) ?: "" ) ) - } else { + } else if (field.type == BaseType.MultiResponseQuestion) { col.add( - UIRadioButton( - "responses[$index].answers[0]", - value = field.answers?.get(index2) ?: "", + UICheckbox( + "responses[$index].answers[$index2]", label = field.answers?.get(index2) ?: "" ) ) } } } + + /* val isLastField = index == pollDto.inputFields?.size?.minus(1) + + if (isLastField) { + col.add( + UIInput( + "responses[$index].answers[$index]", + ) + ) + } + */ + fieldSetQuestions.add(UIRow().add(col)) fieldset.add(fieldSetQuestions) } @@ -355,4 +367,4 @@ class PollResponsePageRest : AbstractDynamicPageRest() { } return poll } -} +} \ No newline at end of file diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PQuestion.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PQuestion.kt new file mode 100644 index 0000000000..db3179467f --- /dev/null +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PQuestion.kt @@ -0,0 +1,25 @@ +package org.projectforge.rest.poll.types + +import com.fasterxml.jackson.databind.ObjectMapper + +class PQuestion ( + val uid: String? = null, + val question: String? = "", + val pType: PreType? = PreType.Neujahrsfeier, + var answers: MutableList? = mutableListOf(""), + var parent: String? = null, + var isRequired: Boolean? = false, + var numberOfSelect: Int? = 1, +) { + fun toObject(string: String): PQuestion { + return ObjectMapper().readValue(string, PQuestion::class.java) + } + + fun toJson(): String { + return ObjectMapper().writeValueAsString(this) + } +} + +enum class PreType { + Neujahrsfeier, Sommerfest, Teamessen, +} diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PremadeQuestions.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PremadeQuestions.kt index 987d2b836c..d83f02f538 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PremadeQuestions.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PremadeQuestions.kt @@ -26,7 +26,7 @@ package org.projectforge.rest.poll.types import java.util.UUID -val PREMADE_QUESTIONS = mapOf( +val Neujahrsfeier = mapOf( "HAS_FOOD" to Question( uid = UUID.randomUUID().toString(), question = "Was willst du essen?", @@ -64,3 +64,64 @@ val PREMADE_QUESTIONS = mapOf( answers = mutableListOf("Ja", "Nein") ), ) + +val Sommerfest = mapOf( + "IS_IN" to Question( + uid = UUID.randomUUID().toString(), + question = "Nimmst du teil ?", + type = BaseType.SingleResponseQuestion, + answers = mutableListOf("Ja", "Nein") + ), + "WHAT_TO_EAT" to Question( + uid = UUID.randomUUID().toString(), + question = "Zur Essens Auswahl gibt es", + type = BaseType.SingleResponseQuestion, + answers = mutableListOf("Pizza", "Pasta", "Burger") + ), + "CAN_HAVE_COMPANIONS" to Question( + uid = UUID.randomUUID().toString(), + question = "Kommst du in Begleitung ?", + type = BaseType.SingleResponseQuestion, + answers = mutableListOf("Ja", "Nein") + ), + "HOW_MANY_COMPANIONS" to Question( + uid = UUID.randomUUID().toString(), + question = "Wenn du in Begleitung kommst wie viele ?", + type = BaseType.TextQuestion, + answers = mutableListOf("") + ), + "CAN_STAY_OVERNIGHT" to Question( + uid = UUID.randomUUID().toString(), + question = "Willst du vor ort Ãœbernachten ?", + type = BaseType.SingleResponseQuestion, + answers = mutableListOf("Ja", "Nein") + ), + "HAS_BREAKFAST" to Question( + uid = UUID.randomUUID().toString(), + question = "Wenn du übernachtest möchtest du am nächsten tag Frühstück ?", + type = BaseType.SingleResponseQuestion, + answers = mutableListOf("Ja", "Nein") + ), +) + +val Teamessen = mapOf( + "IS_IN" to Question( + uid = UUID.randomUUID().toString(), + question = "Hast du Zeit und Lust ?", + type = BaseType.SingleResponseQuestion, + answers = mutableListOf("Ja", "Nein") + ), + "WHAT_TO_EAT" to Question( + uid = UUID.randomUUID().toString(), + question = "Zur Essens Auswahl gibt es", + type = BaseType.SingleResponseQuestion, + answers = mutableListOf("Pizza", "Pasta", "Burger") + ), + "CAN_STAY_OVERNIGHT" to Question( + uid = UUID.randomUUID().toString(), + question = "Welcher dieser Termine passt dir am besten ? (Du kannst auch mehrere ankreuzen)", + type = BaseType.MultiResponseQuestion, + answers = mutableListOf("", "") + ), +) + diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/Question.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/Question.kt index f69e487753..433721ec31 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/Question.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/Question.kt @@ -45,6 +45,6 @@ class Question( } enum class BaseType { - TextQuestion, SingleResponseQuestion, MultiResponseQuestion, + TextQuestion, SingleResponseQuestion, MultiResponseQuestion } From b2e9145aa84e6ad170ad3bebf7986ec456533b2b Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Thu, 18 Apr 2024 12:02:33 +0200 Subject: [PATCH 03/16] =?UTF-8?q?Excel=20Export=20with=20Annotations,=20Ne?= =?UTF-8?q?w=20Poll=20Design,=20Email=20for=20FAU=20and=20Attendes,=20Cust?= =?UTF-8?q?om=20Email=20Field=20in=20Poll=20Creation,=20Annotation=20field?= =?UTF-8?q?=20under=20Every=20Single=20or=20Multi=20Response=20question,?= =?UTF-8?q?=20New=20Premadequestions=20like=20Sommerfest,=20Fr=C3=BChlings?= =?UTF-8?q?fest=20or=20Neujahrsfeier.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/resources/I18nResources.properties | 64 +++- .../resources/I18nResources_de.properties | 43 ++- .../kotlin/org/projectforge/rest/poll/Poll.kt | 4 +- .../projectforge/rest/poll/PollCronJobs.kt | 3 +- .../rest/poll/PollInfoPageRest.kt | 2 +- .../projectforge/rest/poll/PollMailService.kt | 2 +- .../projectforge/rest/poll/PollPageRest.kt | 306 ++++++++++++++---- .../rest/poll/PollResponsePageRest.kt | 33 +- .../rest/poll/excel/ExcelExport.kt | 72 +++-- .../projectforge/rest/poll/types/PQuestion.kt | 2 +- .../rest/poll/types/PollResponse.kt | 3 +- .../rest/poll/types/PremadeQuestions.kt | 36 +-- .../projectforge/rest/poll/types/Question.kt | 3 +- 13 files changed, 421 insertions(+), 152 deletions(-) diff --git a/projectforge-business/src/main/resources/I18nResources.properties b/projectforge-business/src/main/resources/I18nResources.properties index e328ad239d..ddf625ad02 100644 --- a/projectforge-business/src/main/resources/I18nResources.properties +++ b/projectforge-business/src/main/resources/I18nResources.properties @@ -100,6 +100,7 @@ uptodate=up-to-date wizard=Wizard # Common +location=Location akquise=Acquisition changes=Changes charactersLeft=characters left. @@ -278,7 +279,7 @@ timePeriod=Time period timestamp=Time stamp timezone=Time zone tip=Tip -title=Title +titel=Title totalSum=Total sum undefined=undefined until=until @@ -2021,20 +2022,57 @@ poll.groupAttendees=Attendee Groups poll.guide=Poll Guide poll.infopage=Info Page poll.location=Location -poll.mail.ended.content=

Dear Attendees,

\ -

We wanted to let you know that the poll "{0}" created by {1} has now ended. Thank you to everyone who participated and provided valuable input.

\ -

If you missed the deadline to submit your responses, we encourage you to still share your thoughts with us. While we may not be able to include your responses in the official results, your feedback is still valuable for future polls.

\ -

Thank you again for your participation.

\ -

Best regards,

\ -

{1}

-poll.mail.ended.subject=Poll ended -poll.mail.endingSoon.content=

Dear Attendees,

\ -

This is a friendly reminder that the poll "{0}" created by {1} is ending soon, on {2}. Please make sure to submit your responses before the deadline.

\ -

If you have not yet had a chance to participate, please take a few moments to do so before the poll closes. Your input is important and valued.

\ -

{3}

\ -

Thank you for your attention, and have a great day!

\ + + +poll.mail.ended.fullAccess.subject=The survey titled "{0}" has ended +poll.mail.ended.fullAccess.content=This email is for all full-access users. Attached is the Excel spreadsheet containing the survey results. + +poll.annotations.description=In the annotation field, you can add information that you believe is not clearly defined in the responses or if you wish to provide important information, such as allergies. + +poll.email-subject-tooltip=Here you can choose the subject of the email yourself. Use {0} to insert the survey title and {1} to set the automatic end date of the survey - which is the deadline for voting. \ + + +poll.email-content-tooltip=Here you can determine the content of the email yourself. Use {0} to call the survey title. With {1}, you can display the survey creator, with {2} you create a direct link to the survey.\ + \ Use {3} to retrieve the survey description, and {4} to automatically retrieve the end date of the survey, i.e., the deadline for voting. + +poll.fullAccessUser.tooltip=Full access users have the ability to close the survey, view the Excel results, and add or remove individuals (they cannot remove themselves). They are not counted as participants.\ + \ If these individuals are also supposed to vote, they must be additionally registered as participants. + +poll.fullAccessgroups.tooltip=Full access groups include, for example, the BUs, the MUK team, or the Marketing team. Individuals with full access can close the survey prematurely, view the Excel results, or add users. \ + If these individuals are supposed to vote, they must be added as participants. + +poll.attendees.tooltip=Attendees can only vote. + +poll.groupAttendees.tooltip=Group attendees are a convenient way to add entire groups, such as BUs, all at once instead of individually. Attendees can only vote. + +poll.premadeQuestion.tooltip=In this menu, you will find frequently used templates that help you create the survey quickly. If questions are missing in the template, you can also add them. Additionally,\ + \ you have the option to revise or correct questions if they do not apply to your situation in the template. + +poll.questionType.tooltip=Question types offer various options for questions, including single-choice questions, multiple-choice questions, and text questions. You can easily add them if a question is missing\ + \ in the template or if you want to create your own survey without using a template. + + + +annotations=Annotations + +email-content-field=Email Content +email-subject-field=Email Subject + +poll.premade.template=Add Template +poll.questionTemplate=Templates +poll.templateTypeNeujahrsfeier=New Year's Party +poll.templateTypeSommerfest=Summer Party +poll.templateTypeTeamessen=Team Dinner + +poll.mail.ended.subject=The survey titled "{0}" has ended +poll.mail.ended.content=

Dear participants,

\ +

We would like to inform you that the survey "{0}", created by {1}, has now ended. Thank you to all who participated.

\ +

You can still view your results/answers, but you can no longer edit them.

\ +

If you forgot to vote or perhaps want to make some changes, please contact an authorized person or the creator, in this case "{1}".

\ +
\

Best regards,

\

{1}

+ poll.mail.endingSoon.subject=Poll ending in {0} days poll.mail.update.content=

Dear Attendees,

\

We wanted to let you know that the poll "{0}" was edited recently.

\ diff --git a/projectforge-business/src/main/resources/I18nResources_de.properties b/projectforge-business/src/main/resources/I18nResources_de.properties index 10afc1604c..70843de279 100644 --- a/projectforge-business/src/main/resources/I18nResources_de.properties +++ b/projectforge-business/src/main/resources/I18nResources_de.properties @@ -2114,21 +2114,53 @@ poll.groupAttendees=Teilnehmergruppen poll.guide=Anleitung poll.infopage=Infoseite poll.location=Ort +location.description=Hier kann man die Location vom Event eintragen. location=Ort owner=Ersteller +annotations=Anmerkungen + +email-content-field=Email Inhalt +email-subject-field=Email Titel + +poll.annotations.description=Im Anmerkungsfeld kannst du Informationen hinzufügen, die in den Antworten nicht klar definiert wurden deiner Meinung nach oder wenn du wichtige informationen hinzugeben möchtest wie zum Beispiel Allergien. + +poll.email-subject-tooltip= Hier kannst du den Betreff der E-Mail selbst wählen. Verwende {0}, um den Titel der Umfrage einzufügen, und {1}, um das automatische Enddatum der Umfrage zu setzen -\ + \ das ist die Deadline für die Abstimmung. +poll.email-content-tooltip=Hier kannst du den Inhalt der E-Mail selbst bestimmen. Verwende {0}, um den Titel der Umfrage aufzurufen. Mit {1} kannst du den Ersteller der Umfrage anzeigen, mit {2} \ + erstellst du einen direkten Link zur Umfrage. Nutze {3}, um die Beschreibung der Umfrage abzurufen, und {4} um automatische das Enddatum der Umfrage abzurufen, also die Deadline für die Abstimmung. \ + +poll.fullAccessUser.tooltip=Vollzugriffbenutzer:innen haben die Möglichkeit, die Umfrage zu beenden, die Excel-Ergebnisse einzusehen sowie Personen hinzuzufügen oder zu entfernen\ + \ (Sie können sich nicht selbst entfernen). Sie werden nicht als Teilnehmer gezählt. Wenn diese Personen auch abstimmen sollen, müssen sie zusätzlich als Teilnehmer eingetragen werden. + +poll.fullAccessgroups.tooltip=Zu den Vollzugriffgruppen gehören beispielsweise die BU's, das MUK-Team oder auch das Marketing-Team. Personen mit Vollzugriff können die Umfrage vorzeitig beenden,\ + \ die Excel-Ergebnisse einsehen oder auch Nutzer hinzufügen. Wenn diese Personen abstimmen sollen, müssen sie diese noch als Teilnehmer hinzufügen. + +poll.attendees.tooltip=Teilnehmer:innen können nur abstimmen. + +poll.groupAttendees.tooltip=Teilnehmergruppen sind eine praktische Möglichkeit, nicht jede Person einzeln hinzufügen, sondern ganze Gruppen wie BUs auf einmal. Teilnehmer können nur abstimmen. + +poll.premadeQuestion.tooltip=In diesem Menü findest du häufig verwendete Vorlagen, die dir dabei helfen, die Umfrage schnell zu erstellen. Falls Fragen in der Vorlage fehlen, kannst du sie auch hinzufügen. \ + Außerdem hast du die Möglichkeit, Fragen zu überarbeiten oder zu korrigieren, wenn sie in der Vorlage nicht auf deine Situtation zutreffen. + +poll.questionType.tooltip=Fragetypen bieten verschiedene Möglichkeiten für Fragen, darunter Einzelantwortfragen, Mehrfachantwortfragen und Textfragen. Du kannst sie einfach hinzufügen, \ + falls eine Frage in der Vorlage fehlt oder wenn du deine eigene Umfrage erstellen möchtest, ohne eine Vorlage zu verwenden. + +poll.error.cantremoveyourself=Du kannst dich nicht selber als Full Access User entfernen bitte einen anderen Full Access User oder den Erstelle dieser Umfrage dies für dich zu tun. + + poll.premade.template=Vorlage hinzufügen poll.questionTemplate=Vorlagen poll.templateTypeNeujahrsfeier=Neujahrsfeier poll.templateTypeSommerfest=Sommerfest poll.templateTypeTeamessen=Teamessen - #Umfrage wurde erstellt -poll.mail.created.subject=Sie wurden zu einer Umfrage eingeladen mit dem Titel "{0}" +poll.mail.created.subject=

Sie wurden zu einer Umfrage eingeladen mit dem Titel "{0}" eingeladen.

poll.mail.created.content=

Liebe Teilnehmerinnen und Teilnehmer,

\

Wir möchten Ihnen mitteilen, dass eine Umfrage erstellt wurde mit dem Titel "{0}", und Sie wurden herzlichst eingeladen bei dieser Abzustimmen.

\

Hier kommst du direkt zur Umfrage

\ + \

\

Mit Freundlichen Grüßen,

\

{1}

@@ -2169,8 +2201,7 @@ poll.mail.ended.content=

Liebe Teilnehmerinnen und Teilnehmer,

\

{1}

poll.mail.ended.fullAccess.subject=Die Umfrage mit dem Title "{0}" wurde Beendet -poll.mail.ended.fullAccess.content=

Den Text nochmal ändern damit die Full Access\ -\User bescheid wissen das diese Email nur die Excel enthält

+poll.mail.ended.fullAccess.content=Diese Email ist für Alle Vollzugriffnutzer:innen. Im Anhang ist die Excel Tabelle mit den Ergebnissen der Umfrage. #Umfrage abgelaufen @@ -2198,12 +2229,14 @@ poll.question.text=Eingabe poll.question.single=Frage poll.question.multi=Frage +poll.running= +poll.finished= poll.question.texttitle= Text Frage poll.question.singletitel= Eine Antwort Frage poll.question.multititle= Mehrere Antworten Frage - +poll.error.oneAttendeRequired=Bitte füge mindestens einen Teilnehmer hinzu #Kalender Monate January=Januar diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/Poll.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/Poll.kt index d5566b51e8..00dba155f6 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/Poll.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/Poll.kt @@ -40,6 +40,8 @@ class Poll( var deadline: LocalDate? = null, var state: PollDO.State? = PollDO.State.RUNNING, var questionType: String? = null, + var customemailsubject: String? = null, + var customemailcontent: String? = null, var prequestionType: String? = null, var inputFields: MutableList? = mutableListOf(), var fullAccessGroups: List? = null, @@ -78,4 +80,4 @@ class Poll( fun isFinished(): Boolean { return state == PollDO.State.FINISHED } -} +} \ No newline at end of file diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollCronJobs.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollCronJobs.kt index d4689e210d..eb083bdfa3 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollCronJobs.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollCronJobs.kt @@ -65,13 +65,14 @@ class PollCronJobs { * Cron job for daily stuff */ - @Scheduled(cron = "0 0 1 * * *") // 1am everyday + @Scheduled(cron = "0 0 */12 * * *") //Alle 12 Stunden fun dailyCronJobs() { log.info("Start daily cron jobs") cronDeletePolls() cronEndPolls() } + /** * Method to end polls after deadline */ diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollInfoPageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollInfoPageRest.kt index e5e58afd71..85bc0381ec 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollInfoPageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollInfoPageRest.kt @@ -121,4 +121,4 @@ class PollInfoPageRest : AbstractDynamicPageRest() { return FormLayoutData(null, layout, createServerData(request)) } -} +} \ No newline at end of file diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt index 8038cb993d..9d8a1728a9 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt @@ -129,4 +129,4 @@ class PollMailService { User.restoreEmails(userList, userService) return userList!!.mapNotNull { it.email } } -} +} \ No newline at end of file diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt index 38de4a0bea..1e489d8de8 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt @@ -33,6 +33,7 @@ import org.projectforge.business.poll.filter.PollAssignmentFilter import org.projectforge.business.poll.filter.PollState import org.projectforge.business.poll.filter.PollStateFilter import org.projectforge.business.user.service.UserService +import org.projectforge.common.i18n.UserException import org.projectforge.framework.access.AccessException import org.projectforge.framework.i18n.translate import org.projectforge.framework.i18n.translateMsg @@ -215,36 +216,99 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. addDefaultParameterFields(dto, fieldset, isRunning = dto.state == PollDO.State.RUNNING) - fieldset - .add(UISelect.createUserSelect(lc, "fullAccessUsers", true, "poll.fullAccessUsers")) - .add(UISelect.createGroupSelect(lc, "fullAccessGroups", true, "poll.fullAccessGroups")) - .add(UISelect.createUserSelect(lc, "attendees", true, "poll.attendees")) - .add(UISelect.createGroupSelect(lc, "groupAttendees", true, "poll.groupAttendees")) - - if(!dto.isAlreadyCreated()) { + val rowWidth = UILength(xs = 12, sm = 12, md = 12, lg = 12) + val colWidth = UILength(xs = 12, sm = 12, md = 6, lg = 6) - fieldset.add( + fieldset + .add( UIRow() .add( - UICol(UILength(xs = 10, sm = 10, md = 10, lg = 10)) + UICol(colWidth) + .add( + UISelect.createUserSelect( + lc, + "fullAccessUsers", + true, + "poll.fullAccessUsers", + tooltip = "poll.fullAccessUser.tooltip" + ) + ) + ) + .add( + UICol(colWidth) .add( - UISelect( - "prequestionType", - values = PreType.values() - .map { UISelectValue(it, translateMsg("poll.templateType" + it.name)) }, - label = "poll.questionTemplate" + UISelect.createGroupSelect( + lc, + "fullAccessGroups", + true, + "poll.fullAccessGroups", + tooltip = "poll.fullAccessgroups.tooltip" ) ) ) ) + .add( + UIRow() + .add( + UICol(colWidth) + .add( + UISelect.createUserSelect( + lc, + "attendees", + true, + "poll.attendees", + tooltip = "poll.attendees.tooltip" + ) + ) + ) + .add( + UICol(colWidth) + .add( + UISelect.createGroupSelect( + lc, + "groupAttendees", + true, + "poll.groupAttendees", + tooltip = "poll.groupAttendees.tooltip" + ) + ) + ) + ) + + if(!dto.isAlreadyCreated()) { + fieldset .add( UIRow() .add( - UICol(UILength(xs = 11, sm = 11, md = 11, lg = 11)) + UICol(colWidth) + .add( + UISelect( + "prequestionType", + values = PreType.values() + .map { UISelectValue(it, translateMsg("poll.templateType" + it.name)) }, + label = "poll.questionTemplate", + tooltip = "poll.premadeQuestion.tooltip" + ) + ) + ) + .add( + UICol(colWidth) + .add( + UISelect( + "questionType", + values = BaseType.values() + .map { UISelectValue(it, translateMsg("poll.questionType." + it.name)) }, + label = "poll.questionType", + tooltip = "poll.questionType.tooltip" + ) + ) ) + ) + .add( + UIRow() .add( - UICol(UILength(xs = 2, sm = 2, md = 2, lg = 2)) + UICol(colWidth) .add( UIButton.createDefaultButton( id = "template-button", @@ -257,47 +321,79 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. ) ) ) + .add( + UICol(colWidth) + .add( + UIButton.createDefaultButton( + id = "add-question-button", + title = "poll.button.addQuestion", + responseAction = ResponseAction( + "${Rest.URL}/poll/add", + targetType = TargetType.PUT + ), + default = false + ) + ) + ) ) - - fieldset.add( - UIRow() - .add( - UICol(UILength(xs = 10, sm = 10, md = 10, lg = 10)) - .add( - UISelect( - "questionType", - values = BaseType.values() - .map { UISelectValue(it, translateMsg("poll.questionType." + it.name)) }, - label = "poll.questionType" - ) - ) - ) - ) - - - - .add( - UIRow() - .add( - UICol(UILength(xs = 11, sm = 11, md = 11, lg = 11)) - ) - .add( - UICol(UILength(xs = 3, sm = 3, md = 3, lg = 3)) - .add(UISpacer()) - .add( - UIButton.createDefaultButton( - id = "add-question-button", - title = "poll.button.addQuestion", - responseAction = ResponseAction( - "${Rest.URL}/poll/add", - targetType = TargetType.PUT - ), - default = false - ) + .add( + UIRow() + .add( + UICol(colWidth) + .add( + UIInput( + "customemailsubject", + required = false, + label = "email-subject-field", + tooltip = "poll.email-subject-tooltip" ) - ) - ) + ) + ) + .add( + UICol(colWidth) + .add( + UISpacer() + ) + ) + ) + .add( + UIRow() + .add( + UICol(colWidth) + .add( + UITextArea ( + "customemailcontent", + label = "email-content-field", + tooltip = "poll.email-content-tooltip", + rows = 12, + maxRows = 60 + ) + ) + ) + .add( + UICol(colWidth) + .add( + UISpacer() + ) + ) + ) + } + + + val content = "Liebe Teilnehmerinnen und Teilnehmer\n" + + "Wir möchten Ihnen mitteilen, dass eine Umfrage erstellt wurde mit dem Titel \"{0}\", und Sie wurden herzlichst eingeladen bei dieser Abzustimmen.\n" + + "\n" + + "Die Umfrage zu welcher sie Eingeladen worden endet am {4} eine Kurze Beschreibung um was es geht gibt es hier nochmal '{3}'\n" + + "Hier kommst du direkt zur {2}\n" + + "\n" + + "Mit Freundlichen Grüßen\n" + + "{1}" + dto.customemailcontent = content + + val subject = "Sie wurden zu einer Umfrage eingeladen mit dem Titel \"{0}\" eingeladen.

" + dto.customemailsubject = subject + addQuestionFieldset(layout, dto, fieldset) layout.watchFields.add("delegationUser") @@ -415,10 +511,13 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. } super.onBeforeSaveOrUpdate(request, obj,postData) + } + override fun onAfterSaveOrUpdate(request: HttpServletRequest, obj: PollDO, postData: PostData) { + if (postData.data.state != PollDO.State.FINISHED) { val owner = userService.getUser(obj.owner?.id) @@ -430,7 +529,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. val mailTo = pollMailService.getAllFullAccessEmails(postData.data) - mailSubject = translateMsg("poll.mail.update.subject") + mailSubject = translateMsg("poll.mail.update.subject", obj.description, obj.deadline) mailContent = translateMsg( "poll.mail.update.content", obj.title, owner?.displayName ) @@ -439,18 +538,64 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. } else { + val dto = postData.data - val mailTo = pollMailService.getAllMails(postData.data) + val subject = dto.customemailsubject + val content = dto.customemailcontent + + if (subject.isNullOrEmpty() && content.isNullOrEmpty()) { + val mailTo = pollMailService.getAllMails(postData.data) + + mailSubject = translateMsg("poll.mail.created.subject", obj.title, obj.description, obj.deadline) + mailContent = translateMsg( + "poll.mail.created.content", + obj.title, + owner?.displayName, + "http://localhost:8080/react/pollResponse/dynamic/?pollId=${obj.id}" + ) + pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) + + } else if (subject.isNullOrEmpty() && !content.isNullOrEmpty()) { + val mailTo = pollMailService.getAllMails(postData.data) + + mailSubject = translateMsg("poll.mail.created.subject", obj.title, obj.deadline) + mailContent = translateMsg(content, + obj.title, + owner?.displayName, + "

Hier kommst du direkt zur Umfrage

", + obj.description, + obj.deadline + ) + pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) + } else if (!subject.isNullOrEmpty() && content.isNullOrEmpty()) { + val mailTo = pollMailService.getAllMails(postData.data) + + mailSubject = translateMsg("$subject", obj.title, obj.deadline) + mailContent = translateMsg("poll.mail.created.content", + obj.title, + owner?.displayName, + "http://localhost:8080/react/pollResponse/dynamic/?pollId=${obj.id}", + obj.description, + obj.deadline + ) + pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) + + } else if (!subject.isNullOrEmpty() && !content.isNullOrEmpty()){ + val mailTo = pollMailService.getAllMails(postData.data) + + mailSubject = translateMsg("$subject", obj.title, obj.deadline) + mailContent = translateMsg(content, + obj.title, + owner?.displayName, + "

Hier kommst du direkt zur Umfrage

", + obj.description, + obj.deadline + ) + pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) + + } - mailSubject = translateMsg("poll.mail.created.subject", obj.title) - mailContent = translateMsg( - "poll.mail.created.content", - obj.title, - owner?.displayName, - "http://localhost:8080/react/pollResponse/dynamic/?pollId=${obj.id}" - ) - pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) } } super.onAfterSaveOrUpdate(request, obj, postData) @@ -729,11 +874,32 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. private fun addDefaultParameterFields(pollDto: Poll, fieldset: UIFieldset, isRunning: Boolean) { + + val colWidth = UILength(xs = 12, sm = 12, md = 6, lg = 6) if (isRunning) { fieldset .add(lc, "title", "description", "location") - .add(UISelect.createUserSelect(lc, "owner", false, "poll.owner")) - .add(lc, "deadline") + .add( + UIRow() + .add( + UICol(colWidth) + .add( + UISelect.createUserSelect( + lc, + "owner", + false, + "poll.owner", + ) + ) + ) + .add( + UICol(colWidth) + .add( + lc, + "deadline", + ) + ) + ) } else { fieldset .add(UIReadOnlyField(value = pollDto.title, label = "titel", dataType = UIDataType.STRING)) @@ -759,6 +925,8 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. val pollDO = PollDO() pollDto.copyTo(pollDO) + //Fua entfernen + return if (!pollDao.hasFullAccess(pollDO)) { // no full access user UILayout.UserAccess(insert = false, update = false, delete = false, history = false) @@ -776,8 +944,4 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. } } } -} - - - - +} \ No newline at end of file diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt index 2cb797df19..35ef5a4ba1 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt @@ -128,7 +128,9 @@ class PollResponsePageRest : AbstractDynamicPageRest() { .add(UIReadOnlyField(value = pollDto.deadline.toString(), label = translateMsg("poll.deadline"))) - if (!pollDto.isFinished() && ThreadLocalUserContext.userId === questionOwnerId) { + + /* Aktuell nicht benutzbar auskommentiert bis es behoben wird + if (pollDto.isFinished() && ThreadLocalUserContext.userId === questionOwnerId && pollDao.hasFullAccess(pollData)) { val fieldSetDelegationUser = UIFieldset(title = "poll.userDelegation") fieldSetDelegationUser.add( UIInput( @@ -154,6 +156,8 @@ class PollResponsePageRest : AbstractDynamicPageRest() { layout.add(fieldSetDelegationUser) } + */ + val pollResponse = PollResponse() pollResponse.poll = pollData @@ -164,7 +168,6 @@ class PollResponsePageRest : AbstractDynamicPageRest() { pollResponse.copyFrom(it) } - var index = 0 pollDto.inputFields?.forEachIndexed { index, field -> val fieldSetQuestions = UIFieldset(title = field.question) val questionAnswer = QuestionAnswer() @@ -189,40 +192,38 @@ class PollResponsePageRest : AbstractDynamicPageRest() { ) } + var index3 = 0 if (field.type == BaseType.MultiResponseQuestion || field.type === BaseType.SingleResponseQuestion) { field.answers?.forEachIndexed { index2, _ -> if (pollResponse.responses?.get(index)?.answers?.getOrNull(index2) == null) { pollResponse.responses?.get(index)?.answers?.add(index2, false) } - if (field.type == BaseType.SingleResponseQuestion) { + if (field.type == BaseType.MultiResponseQuestion) { col.add( - UIRadioButton( + UICheckbox( "responses[$index].answers[$index2]", - value = field.answers?.get(index2) ?: "", label = field.answers?.get(index2) ?: "" ) ) - } else if (field.type == BaseType.MultiResponseQuestion) { + } else { col.add( - UICheckbox( - "responses[$index].answers[$index2]", + UIRadioButton( + "responses[$index].answers[0]", + value = field.answers?.get(index2) ?: "", label = field.answers?.get(index2) ?: "" ) ) } } - } - - /* val isLastField = index == pollDto.inputFields?.size?.minus(1) - - if (isLastField) { col.add( - UIInput( - "responses[$index].answers[$index]", + UITextArea( + "responses[$index].annotation[0]", + label = "annotations", + additionalLabel = "poll.annotations.description" ) ) + } - */ fieldSetQuestions.add(UIRow().add(col)) fieldset.add(fieldSetQuestions) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt index 86c58d3ccc..a614283153 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt @@ -133,12 +133,19 @@ class ExcelExport { val excelRow = excelSheet.getRow(0) val excelRow1 = excelSheet.getRow(1) + style.alignment = HorizontalAlignment.CENTER var merge = 1 poll.inputFields?.forEach { question -> val answers = question.answers + var totalAnswerLength = question.answers?.sumBy { countWords(it) } ?: 0 + var questionLength = question.question?.let { countWords(it) } if (question.type == BaseType.MultiResponseQuestion || question.type == BaseType.SingleResponseQuestion) { + if (!question.answers!!.contains("Anmerkung")) { + val ind = question.answers!!.size + question.answers!!.add(ind,"Anmerkung") + } var counter = merge question.answers?.forEach { answer -> excelRow1.getCell(counter).setCellValue(answer) @@ -170,36 +177,52 @@ class ExcelExport { excelRow.getCell(0).setCellValue(user.displayName) - excelSheet.autosize(0) var cell = 0 + excelSheet.autosize(0) var largestAnswer = "" poll.inputFields?.forEachIndexed { _, question -> - val questionAnswer = res?.responses?.find { it.questionUid == question.uid } + val questionpossibilities = res?.responses?.find { it.questionUid == question.uid } - if (questionAnswer?.answers.isNullOrEmpty()) { - cell += question.answers?.size ?: 0 - } - questionAnswer?.answers?.forEachIndexed { ind, answer -> - cell++ - if (question.type == BaseType.MultiResponseQuestion) { - if (answer is Boolean && answer == true) { - excelRow.getCell(cell).setCellValue("X") - } - } else if (question.type == BaseType.SingleResponseQuestion) { - if (answer is String && answer.equals(question.answers?.get(ind))) { - excelRow.getCell(cell).setCellValue("X") + var index = 0 + var size = 0; + question.answers?.forEachIndexed { ind, answer -> + index = question.answers!!.size - 1 + cell++ + + + if (question.type == BaseType.MultiResponseQuestion || question.type == BaseType.SingleResponseQuestion) { + if (index == ind && questionpossibilities != null) { + excelSheet.autosize(cell) + excelRow.getCell(cell).setCellValue(questionpossibilities.annotation!!.get(0)) + } } - } else { - excelRow.getCell(cell).setCellValue(answer.toString()) - if (countLines(answer.toString()) > countLines(largestAnswer)) { - largestAnswer = answer.toString() + + if (question.type == BaseType.MultiResponseQuestion) { + questionpossibilities?.answers?.forEach { + excelSheet.autosize(cell) + if (questionpossibilities.answers?.get(ind)!!.equals(true) && ind != index) { + excelRow.getCell(cell).setCellValue("X") + } + } + } else if (question.type == BaseType.SingleResponseQuestion) { + excelSheet.autosize(cell) + if (answer is String && answer.equals(questionpossibilities?.answers?.get(0)) && ind != index) { + excelRow.getCell(cell).setCellValue("X") + } + } else { + if (questionpossibilities?.answers?.isNotEmpty() == true) { + excelSheet.autosize(cell) + excelRow.getCell(cell).setCellValue(questionpossibilities.answers?.get(0).toString()) + if (countLines(answer.toString()) > countLines(largestAnswer)) { + largestAnswer = answer.toString() + } + } } } - excelSheet.autosize(cell) } - } + largestAnswer = "i" val puffer: String = largestAnswer var counterOfBreaking = 0 var counterOfOverlength = 0 @@ -209,13 +232,19 @@ class ExcelExport { // check for line-breaks for (i in pufferSplit.indices) { counterOfBreaking++ - counterOfOverlength += pufferSplit[i].length / 70 + counterOfOverlength += pufferSplit[i].length / 20 } excelRow.setHeight((14 + counterOfOverlength * 14 + counterOfBreaking * 14).toFloat()) } + + private fun countWords(text: String): Int { + return text.split("\\s+".toRegex()).filter { it.isNotEmpty() }.size + } + private fun countLines(str: String): Int { val lines = str.split("\r\n|\r|\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + return lines.size } @@ -240,3 +269,4 @@ class ExcelExport { } } } + diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PQuestion.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PQuestion.kt index db3179467f..31c8ff5dbb 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PQuestion.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PQuestion.kt @@ -22,4 +22,4 @@ class PQuestion ( enum class PreType { Neujahrsfeier, Sommerfest, Teamessen, -} +} \ No newline at end of file diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PollResponse.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PollResponse.kt index 081a1b6584..36e83eab2f 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PollResponse.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PollResponse.kt @@ -53,9 +53,10 @@ class QuestionAnswer { var uid: String? = null var questionUid: String? = "" var answers: MutableList? = mutableListOf() + var annotation: MutableList? = mutableListOf() fun toObject(string: String): QuestionAnswer { return ObjectMapper().readValue(string, QuestionAnswer::class.java) } -} +} \ No newline at end of file diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PremadeQuestions.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PremadeQuestions.kt index d83f02f538..79ccf74ad5 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PremadeQuestions.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PremadeQuestions.kt @@ -23,6 +23,7 @@ package org.projectforge.rest.poll.types +import net.bytebuddy.pool.TypePool.Empty import java.util.UUID @@ -33,12 +34,6 @@ val Neujahrsfeier = mapOf( type = BaseType.SingleResponseQuestion, answers = mutableListOf("Fleisch", "Vegetarisch", "Vegan") ), - "IS_REMOTE" to Question( - uid = UUID.randomUUID().toString(), - question = "Nimmst du remote teil?", - type = BaseType.SingleResponseQuestion, - answers = mutableListOf("Ja", "Nein") - ), "CAN_HAVE_COMPANIONS" to Question( uid = UUID.randomUUID().toString(), question = "Nimmst du eine Begleitung mit? (Name der Begleitung)", @@ -72,21 +67,21 @@ val Sommerfest = mapOf( type = BaseType.SingleResponseQuestion, answers = mutableListOf("Ja", "Nein") ), - "WHAT_TO_EAT" to Question( + "WITH_HOW_MANY" to Question( uid = UUID.randomUUID().toString(), - question = "Zur Essens Auswahl gibt es", - type = BaseType.SingleResponseQuestion, - answers = mutableListOf("Pizza", "Pasta", "Burger") + question = "Kommst du in Begleitung ? Wenn ja mit wie vielen ?", + type = BaseType.TextQuestion, + answers = mutableListOf("") ), - "CAN_HAVE_COMPANIONS" to Question( + "CHILDRENS" to Question( uid = UUID.randomUUID().toString(), - question = "Kommst du in Begleitung ?", + question = "Sind Kinder unter deiner Begleitpersonen ?", type = BaseType.SingleResponseQuestion, answers = mutableListOf("Ja", "Nein") ), - "HOW_MANY_COMPANIONS" to Question( + "HOW_OLD_ARE_THE_CHILDS" to Question( uid = UUID.randomUUID().toString(), - question = "Wenn du in Begleitung kommst wie viele ?", + question = "Wenn du Kinder unter deiner Begleitung hast wie alt sind diese ?", type = BaseType.TextQuestion, answers = mutableListOf("") ), @@ -96,12 +91,18 @@ val Sommerfest = mapOf( type = BaseType.SingleResponseQuestion, answers = mutableListOf("Ja", "Nein") ), - "HAS_BREAKFAST" to Question( + "WHERE_DO_YOU_WANT_TO_STAY" to Question( uid = UUID.randomUUID().toString(), - question = "Wenn du übernachtest möchtest du am nächsten tag Frühstück ?", + question = "Wenn du übernachtest wo möchtest du übernachten ?", type = BaseType.SingleResponseQuestion, - answers = mutableListOf("Ja", "Nein") + answers = mutableListOf("In meinem eigenen Van/Mobil", "Ich bräuchte eine Übernachtungsmöglichkeit", "Ich komme irgendwo anders unter") ), + "WHAT_DO_YOU_EAT" to Question ( + uid = UUID.randomUUID().toString(), + question = "Welche Art von Ernährung bevorzugst du ?", + type = BaseType.SingleResponseQuestion, + answers = mutableListOf("Omnivor (Fleisch)", "Pescetarier (Fisch)", "Vegetarisch", "Vegan") + ) ) val Teamessen = mapOf( @@ -124,4 +125,3 @@ val Teamessen = mapOf( answers = mutableListOf("", "") ), ) - diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/Question.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/Question.kt index 433721ec31..48b41d5bfe 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/Question.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/Question.kt @@ -46,5 +46,4 @@ class Question( enum class BaseType { TextQuestion, SingleResponseQuestion, MultiResponseQuestion -} - +} \ No newline at end of file From 50e9512493c116531a1877feb0f53d7850dd3ed7 Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Thu, 25 Apr 2024 08:33:48 +0200 Subject: [PATCH 04/16] Pull Request fixes --- .../main/resources/I18nResources.properties | 59 +--------- .../resources/I18nResources_de.properties | 109 +----------------- .../projectforge/rest/AddressViewPageRest.kt | 5 +- .../projectforge/rest/poll/PollPageRest.kt | 5 +- .../types/{PQuestion.kt => PreQuestion.kt} | 6 +- 5 files changed, 14 insertions(+), 170 deletions(-) rename projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/{PQuestion.kt => PreQuestion.kt} (79%) diff --git a/projectforge-business/src/main/resources/I18nResources.properties b/projectforge-business/src/main/resources/I18nResources.properties index ddf625ad02..6acc28ddee 100644 --- a/projectforge-business/src/main/resources/I18nResources.properties +++ b/projectforge-business/src/main/resources/I18nResources.properties @@ -13,19 +13,15 @@ validation.error.range.integerOutOfRange=Value out of range {0}-{1}. validation.error.range.integerToHigh=Value must not be higher than {0}. validation.error.range.integerToLow=Value must not be lower than {0}. validation.required.valueNotPresent=Value ''{0}'' not present. - # React UI select.placeholder=Select... table.showing=Showing - # own validations: bicvalidator.wronglength=The field ''${label}'' must be 8 or 11 characters long. ibanvalidator.wronglength.de=The field ''${label}'' starts with ''DE'', but a german IBAN must have 22 characters. - # Currency format currencyConverter.percentage.help=You can enter amounts as well as percent values (e. g. 10%). currencyFormat={0,number,,##0.00} - # Not internationalized properties: exception.notYetSupported=Not yet supported. menu.adminGuide=Administration guide @@ -35,7 +31,6 @@ menu.projectDocumentation=Project docs menu.sqlConsole=SQL console menu.userGuide=Handbuch message.notYetImplemented=Not yet implemented. - # Buttons: add=Add assign=Assign @@ -98,9 +93,10 @@ updateAndNext=Update and next upload=Upload uptodate=up-to-date wizard=Wizard - # Common location=Location +no=No +yes=Yes akquise=Acquisition changes=Changes charactersLeft=characters left. @@ -188,14 +184,6 @@ moreEntriesAvailable=More entries available. name=Name new=New nickname=Nickname - - - - - - - - notEnded=not ended nothingFound=Nothing found. notLoggedIn=Not logged in. @@ -279,7 +267,7 @@ timePeriod=Time period timestamp=Time stamp timezone=Time zone tip=Tip -titel=Title +title=Title totalSum=Total sum undefined=undefined until=until @@ -1982,17 +1970,10 @@ plugins.teamcal.title.add=Add calendar plugins.teamcal.title.edit=Edit team calendar plugins.teamcal.title.heading=Calendar plugins.teamcal.title.list=List of calendars - - - - - # poll plugin TextQuestion=Text Question SingleResponseQuestion=Single Response Question MultiResponseQuestion=Multiple Choice Question - - poll=Poll poll.access=Access poll.answer=Option @@ -2022,48 +2003,30 @@ poll.groupAttendees=Attendee Groups poll.guide=Poll Guide poll.infopage=Info Page poll.location=Location - - poll.mail.ended.fullAccess.subject=The survey titled "{0}" has ended poll.mail.ended.fullAccess.content=This email is for all full-access users. Attached is the Excel spreadsheet containing the survey results. - poll.annotations.description=In the annotation field, you can add information that you believe is not clearly defined in the responses or if you wish to provide important information, such as allergies. - poll.email-subject-tooltip=Here you can choose the subject of the email yourself. Use {0} to insert the survey title and {1} to set the automatic end date of the survey - which is the deadline for voting. \ - - poll.email-content-tooltip=Here you can determine the content of the email yourself. Use {0} to call the survey title. With {1}, you can display the survey creator, with {2} you create a direct link to the survey.\ \ Use {3} to retrieve the survey description, and {4} to automatically retrieve the end date of the survey, i.e., the deadline for voting. - poll.fullAccessUser.tooltip=Full access users have the ability to close the survey, view the Excel results, and add or remove individuals (they cannot remove themselves). They are not counted as participants.\ \ If these individuals are also supposed to vote, they must be additionally registered as participants. - poll.fullAccessgroups.tooltip=Full access groups include, for example, the BUs, the MUK team, or the Marketing team. Individuals with full access can close the survey prematurely, view the Excel results, or add users. \ If these individuals are supposed to vote, they must be added as participants. - poll.attendees.tooltip=Attendees can only vote. - poll.groupAttendees.tooltip=Group attendees are a convenient way to add entire groups, such as BUs, all at once instead of individually. Attendees can only vote. - poll.premadeQuestion.tooltip=In this menu, you will find frequently used templates that help you create the survey quickly. If questions are missing in the template, you can also add them. Additionally,\ \ you have the option to revise or correct questions if they do not apply to your situation in the template. - poll.questionType.tooltip=Question types offer various options for questions, including single-choice questions, multiple-choice questions, and text questions. You can easily add them if a question is missing\ \ in the template or if you want to create your own survey without using a template. - - - annotations=Annotations - email-content-field=Email Content email-subject-field=Email Subject - poll.premade.template=Add Template poll.questionTemplate=Templates poll.templateTypeNeujahrsfeier=New Year's Party poll.templateTypeSommerfest=Summer Party poll.templateTypeTeamessen=Team Dinner - poll.mail.ended.subject=The survey titled "{0}" has ended poll.mail.ended.content=

Dear participants,

\

We would like to inform you that the survey "{0}", created by {1}, has now ended. Thank you to all who participated.

\ @@ -2072,7 +2035,6 @@ poll.mail.ended.content=

Dear participants,

\
\

Best regards,

\

{1}

- poll.mail.endingSoon.subject=Poll ending in {0} days poll.mail.update.content=

Dear Attendees,

\

We wanted to let you know that the poll "{0}" was edited recently.

\ @@ -2094,26 +2056,15 @@ poll.questionType=Question Type poll.questionType.TextQuestion=Text Question poll.questionType.SingleResponseQuestion=Single response Question poll.questionType.MultiResponseQuestion=Multiple Choice Questions - poll.question.texttitle= Text Questions poll.question.singletitel= Singel response Question poll.question.multititle= Multiple Choice Questions - poll.question.text=Question poll.question.single=Question poll.question.multi=Question - poll.question.TextQuestion=Answer poll.question.SingleResponseQuestion=Question poll.question.MultiResponseQuestion=Question - - - - - - - - poll.respond=Send responses poll.response.mail.update.content=

Dear {0},

\

I wanted to inform you that Person {2} has updated their answer to Poll {1}.

\ @@ -2738,7 +2689,6 @@ webauthn.registration.button.authenticate=WebAuthn webauthn.registration.button.authenticate.info=You may use any of your registered WebAuthn tokens here (for example Yubikey). webauthn.registration.button.register=Register webauthn.title=WebAuthn (Fido2 etc.) - # (timeable) attributes attr.deletemodal.heading=Would you like to delete this entry? attr.deletemodal.question=Yes: This entry will be deleted and all changes on this page will be saved.
Cancel: This entry will not be deleted and you stay on this page. @@ -2748,7 +2698,6 @@ attr.savemodal.question=Yes: All changes on this page will be saved.
< attr.starttime.alreadyexists.day=There is already an entry with the same date (Day). attr.starttime.alreadyexists.month=There is already an entry with the same date (Month). attr.validFrom=Valid from - birthdayButler.email.content=In the month of {0}, {1} employees are celebrating their birthdays. Attached, you will find the list of birthdays. birthdayButler.email.content.noBirthdaysFound=No birthday entries were found in the month of {0}. birthdayButler.email.opening=New month, new birthday list! @@ -2757,4 +2706,4 @@ birthdayButler.month.response.noEntry=There are no birthday entries for this mon birthdayButler.month.response.nothingSelected=The month is required. birthdayButler.organization.noMatchingUser=There are no users assigned to the organization configured in projectforge.properties. birthdayButler.organization.notSet=No organization is defined in the application.properties. -> projectforge.birthdayButler.organization=Your Organization -birthdayButler.wordDocument.error=Error while creating the Word document. +birthdayButler.wordDocument.error=Error while creating the Word document. \ No newline at end of file diff --git a/projectforge-business/src/main/resources/I18nResources_de.properties b/projectforge-business/src/main/resources/I18nResources_de.properties index 70843de279..e687deafcd 100644 --- a/projectforge-business/src/main/resources/I18nResources_de.properties +++ b/projectforge-business/src/main/resources/I18nResources_de.properties @@ -90,8 +90,6 @@ # system.pluginAdmin.button.deactivate=Deactivate # system.pluginAdmin.title=Plugins # system.statistics.databasePool=Data base pool - - # Wicket: datatable.no-records-found=Keine Einträge gefunden. NavigatorLabel=Zeile ${from} bis ${to} von ${of}. @@ -107,19 +105,15 @@ validation.error.range.integerOutOfRange=Wert au validation.error.range.integerToHigh=Wert darf nicht größer als {0} sein. validation.error.range.integerToLow=Wert darf nicht kleiner als {0} sein. validation.required.valueNotPresent=Wert ''{0}'' nicht gegeben. - # React UI select.placeholder=Auswählen... table.showing=Anzeige - # own validations: bicvalidator.wronglength=Das Feld ''${label}'' muss 8 oder 11 Zeichen lang sein. ibanvalidator.wronglength.de=Das Feld ''${label}'' beginnt mit ''DE''. Eine deutsche IBAN muss jedoch aus 22 Zeichen bestehen. - # Currency format currencyConverter.percentage.help=Es können sowohl Beträge als auch Prozentzahlen (z. B. 10%) eingegeben werden. currencyFormat={0,number,,##0.00} - # Not internationalized properties: ### not translated: exception.notYetSupported=Not yet supported. ### not translated: menu.adminGuide=Administration guide @@ -129,13 +123,11 @@ currencyFormat={0,number,,##0.00} ### not translated: menu.sqlConsole=SQL console ### not translated: menu.userGuide=Handbuch ### not translated: message.notYetImplemented=Not yet implemented. - # Buttons: add=Hinzufügen assign=Zuweisen back=Zurück cancel=Abbrechen - create=Anlegen change=Ändern check=Check @@ -144,9 +136,9 @@ close=Schlie copy=Kopieren delete=Löschen deselectAll=Alle abwählen -download=Herunterladen +download=Download download.expired=Downloaddatei nicht mehr verfügbar. Bitte Aktion wiederholen. -drop=Verwerfern +drop=Drop execute=Ausführen exportAsPdf=Pdf-Exportieren exportAsXls=Excel-Exportieren @@ -157,7 +149,6 @@ favorite.untitled=unbenannt favorites=Favoriten favorites.saveModification=Änderungen speichern finish=Fertig -Button_Finish=Ja forceDelete=Unwiderruflich löschen import=Importieren login=Anmelden @@ -194,8 +185,6 @@ updateAndNext= upload=Hochladen uptodate=aktuell wizard=Assistent - - State.running=Laufend poll.state.running running=Laufend @@ -380,7 +369,6 @@ username=Benutzer:innenname value=Wert values=Werte weekOfYear=Kalenderwoche - access=Zugriffsrecht access.accessTable=Zugriffstabelle access.exception.demoUserHasNoAccess=Der oder die Demobenutzer:in ist für diese Aktion gesperrt. @@ -2073,14 +2061,6 @@ plugins.teamcal.title.add=Kalender hinzuf plugins.teamcal.title.edit=Team-Kalender bearbeiten plugins.teamcal.title.heading=Kalender plugins.teamcal.title.list=Kalenderliste - - - - - - - - # poll plugin poll=Umfrage poll.access=Zugriff @@ -2095,16 +2075,11 @@ Stelle ebenfalls sicher, dass du Teilnehmer f poll.confirmation.deleteAnswer=Möchtest du diese Antwort wirklich löschen? poll.confirmation.deleteQuestion=Möchtest du diese Frage wirklich löschen? poll.confirmation.finish=Willst du die Umfrage wirklich beenden? - - - - poll.state=Status poll.date=Datum poll.deadline=Umfragen Frist poll.delegationAnswers=Antworten von poll.description=Beschreibung - poll.error.oneQuestionRequired=Mindestens eine Frage ist erforderlich. poll.exception.noAttendee=Dieser Nutzer ist nicht Teil der Umfrage. poll.export.response.poll=Ergebnisse exportieren @@ -2117,44 +2092,30 @@ poll.location=Ort location.description=Hier kann man die Location vom Event eintragen. location=Ort owner=Ersteller - annotations=Anmerkungen - email-content-field=Email Inhalt email-subject-field=Email Titel - poll.annotations.description=Im Anmerkungsfeld kannst du Informationen hinzufügen, die in den Antworten nicht klar definiert wurden deiner Meinung nach oder wenn du wichtige informationen hinzugeben möchtest wie zum Beispiel Allergien. - poll.email-subject-tooltip= Hier kannst du den Betreff der E-Mail selbst wählen. Verwende {0}, um den Titel der Umfrage einzufügen, und {1}, um das automatische Enddatum der Umfrage zu setzen -\ \ das ist die Deadline für die Abstimmung. poll.email-content-tooltip=Hier kannst du den Inhalt der E-Mail selbst bestimmen. Verwende {0}, um den Titel der Umfrage aufzurufen. Mit {1} kannst du den Ersteller der Umfrage anzeigen, mit {2} \ erstellst du einen direkten Link zur Umfrage. Nutze {3}, um die Beschreibung der Umfrage abzurufen, und {4} um automatische das Enddatum der Umfrage abzurufen, also die Deadline für die Abstimmung. \ - poll.fullAccessUser.tooltip=Vollzugriffbenutzer:innen haben die Möglichkeit, die Umfrage zu beenden, die Excel-Ergebnisse einzusehen sowie Personen hinzuzufügen oder zu entfernen\ \ (Sie können sich nicht selbst entfernen). Sie werden nicht als Teilnehmer gezählt. Wenn diese Personen auch abstimmen sollen, müssen sie zusätzlich als Teilnehmer eingetragen werden. - poll.fullAccessgroups.tooltip=Zu den Vollzugriffgruppen gehören beispielsweise die BU's, das MUK-Team oder auch das Marketing-Team. Personen mit Vollzugriff können die Umfrage vorzeitig beenden,\ \ die Excel-Ergebnisse einsehen oder auch Nutzer hinzufügen. Wenn diese Personen abstimmen sollen, müssen sie diese noch als Teilnehmer hinzufügen. - poll.attendees.tooltip=Teilnehmer:innen können nur abstimmen. - poll.groupAttendees.tooltip=Teilnehmergruppen sind eine praktische Möglichkeit, nicht jede Person einzeln hinzufügen, sondern ganze Gruppen wie BUs auf einmal. Teilnehmer können nur abstimmen. - poll.premadeQuestion.tooltip=In diesem Menü findest du häufig verwendete Vorlagen, die dir dabei helfen, die Umfrage schnell zu erstellen. Falls Fragen in der Vorlage fehlen, kannst du sie auch hinzufügen. \ Außerdem hast du die Möglichkeit, Fragen zu überarbeiten oder zu korrigieren, wenn sie in der Vorlage nicht auf deine Situtation zutreffen. - poll.questionType.tooltip=Fragetypen bieten verschiedene Möglichkeiten für Fragen, darunter Einzelantwortfragen, Mehrfachantwortfragen und Textfragen. Du kannst sie einfach hinzufügen, \ falls eine Frage in der Vorlage fehlt oder wenn du deine eigene Umfrage erstellen möchtest, ohne eine Vorlage zu verwenden. - poll.error.cantremoveyourself=Du kannst dich nicht selber als Full Access User entfernen bitte einen anderen Full Access User oder den Erstelle dieser Umfrage dies für dich zu tun. - - poll.premade.template=Vorlage hinzufügen poll.questionTemplate=Vorlagen poll.templateTypeNeujahrsfeier=Neujahrsfeier poll.templateTypeSommerfest=Sommerfest poll.templateTypeTeamessen=Teamessen - #Umfrage wurde erstellt poll.mail.created.subject=

Sie wurden zu einer Umfrage eingeladen mit dem Titel "{0}" eingeladen.

poll.mail.created.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ @@ -2163,9 +2124,6 @@ poll.mail.created.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ \

\

Mit Freundlichen Grüßen,

\

{1}

- - - #Umfrage wurde Bearbeitet poll.mail.update.subject=Umfrage wurde bearbeitet poll.mail.update.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ @@ -2174,8 +2132,6 @@ poll.mail.update.content=

Liebe Teilnehmerinnen und Teilnehmer,

\

Nochmals vielen Dank für Ihre Teilnahme.

\

Mit Freundlichen Grüßen,

\

{1}

- - #Umfrage endet Bald poll.mail.endingSoon.subject=Umfrage endet in {0} Tagen poll.mail.endingSoon.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ @@ -2186,10 +2142,6 @@ poll.mail.endingSoon.content=

Liebe Teilnehmerinnen und Teilnehmer,

\
\

Mit Freundlichen Grüßen,

\

{1}

- - - - #Umfrage beendet poll.mail.ended.subject=Die Umfrage mit dem Title "{0}" wurde Beendet poll.mail.ended.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ @@ -2199,11 +2151,8 @@ poll.mail.ended.content=

Liebe Teilnehmerinnen und Teilnehmer,

\
\

Mit Freundlichen Grüßen,

\

{1}

- poll.mail.ended.fullAccess.subject=Die Umfrage mit dem Title "{0}" wurde Beendet poll.mail.ended.fullAccess.content=Diese Email ist für Alle Vollzugriffnutzer:innen. Im Anhang ist die Excel Tabelle mit den Ergebnissen der Umfrage. - - #Umfrage abgelaufen poll.mail.endedafterdeadline.subject=Die Umfrage mit dem Title "{0}" ist Beendet die Deadline ist abgelaufen poll.mail.endedafterdeadline.content=

Liebe Teilnehmerinnen und Teilnehmer,

\ @@ -2213,8 +2162,6 @@ poll.mail.endedafterdeadline.content=

Liebe Teilnehmerinnen und Teilnehmer,

\

Mit Freundlichen Grüßen,

\

{1}

- - poll.manual.title=Anleitung, um eine Umfrage zu erstellen | Als Erstes muss man den poll.manual.questions=Anlegen. Anschließend werden die Fragen der Umfrage angelegt. Die Fragen können aus verschiedenen Typen bestehen. poll.manual.singleResponse=Eine Frage, bei der man eine Antwort auswählen kann. Beispielsweise für eine simple Ja- oder Nein-Frage. @@ -2223,21 +2170,15 @@ poll.manual.textQuestion=Eine Frage, bei der man mit Freitext antworten kann. Gu poll.other=Andere poll.owner=Ersteller poll.popup.closed=Umfrage wurde bereits beendet - - poll.question.text=Eingabe poll.question.single=Frage poll.question.multi=Frage - poll.running= poll.finished= - poll.question.texttitle= Text Frage poll.question.singletitel= Eine Antwort Frage poll.question.multititle= Mehrere Antworten Frage - poll.error.oneAttendeRequired=Bitte füge mindestens einen Teilnehmer hinzu - #Kalender Monate January=Januar February=Februar @@ -2251,82 +2192,42 @@ September=September October=October November=November December=Dezember - - - #Fragen Dropdown menü name poll.questionType=Fragetypen - #Frage Typen endung poll.question.TextQuestion=Antwort poll.question.SingleResponseQuestion=Frage poll.question.MultiResponseQuestion=Frage poll.question.AnnotationField=Anmerkungen: - #Fragen Container Überschrift TextQuestion=Frage mit freier text Antwort SingleResponseQuestion=Frage mit einer Ankreuzmöglichkeit MultiResponseQuestion=Frage mit mehreren Ankrauzmöglichkeiten - #Fragen erstellung überschrift Question=Fragestellung - #Eingabefeld überschriften/Auswahl möglichkeiten überschrift poll.answer=Auswahl möglichkeit - PollState.RUNNING=Am Laufen question.answers.Yes=Ja question.answers=Ja question.answers.0=Ja question.answers.No=Nein question.answers.1=Nein - - - - - - poll.respond=Antworten abschicken yes=Ja +no=Nein poll.questionType.TextQuestion=Text Frage poll.questionType.SingleResponseQuestion=Einzelantwort Frage poll.questionType.MultiResponseQuestion=Mehrfachauswahl Frage - - - - -### not translated: poll.response.mail.update.content=

Dear {0},

\ -#

I wanted to inform you that Person {2} has updated their answer to Poll {1}.

\ -#

If you were not notified about this,

\ -#

I recommend reaching out to the person directly or adjusting your results accordingly.

\ -#

You can modify your response until "{3}".

\ -#

Best regards,

\ -#

{2}

-### not translated: poll.response.mail.update.subject=Response was edited by {0} poll.response.page=Seite zur Umfrageantwort poll.response.title=Seite zur Umfrageantwort poll.selectUser=Nutzer auswählen - - - poll.title=Titel poll.title.add=Neue Umfrage erstellen poll.title.edit=Umfrage bearbeiten poll.title.list=Umfragen poll.userDelegation=Für andere Nutzer abstimmen poll.yourAnswers=Deine Antworten - - - - - - - - - - - - projectmanagement.personDays=Personentage projectmanagement.personDays.short=PT question.deleteQuestion=Soll das Objekt wirklich unwiderruflich gelöscht werden? @@ -2932,7 +2833,6 @@ webauthn.registration.button.authenticate=WebAuthn webauthn.registration.button.authenticate.info=Du kannst hier registrierte WebAuthn-Token benutzen (z. B. Yubikey). webauthn.registration.button.register=Registrieren webauthn.title=WebAuthn (Fido2 etc.) - # (timeable) attributes attr.deletemodal.heading=Soll dieser Eintrag wirklich gelöscht werden? attr.deletemodal.question=Ja: Der Eintrag wird gelöscht und alle Änderungen auf dieser Seite werden gespeichert.
Abbrechen: Der Eintrag wird nicht gelöscht und Sie bleiben auf dieser Seite. @@ -2942,7 +2842,6 @@ attr.savemodal.question=Ja: Alle attr.starttime.alreadyexists.day=Es existiert bereits ein Eintrag mit gleichem Datum (Tag). attr.starttime.alreadyexists.month=Es existiert bereits ein Eintrag mit gleichem Datum (Monat). attr.validFrom=Gültig ab - birthdayButler.email.content=Im Monat {0} haben {1} Mitarbeiter Geburtstag. Im Anhang findest du die Liste der Geburtstage. birthdayButler.email.content.noBirthdaysFound=Im Monat {0} wurden keine Geburtstagseinträge gefunden. birthdayButler.email.opening=Ein neuer Monat, eine neue Geburtstagsliste! @@ -2951,4 +2850,4 @@ birthdayButler.month.response.noEntry=Es gibt keine Geburtstagseintr birthdayButler.month.response.nothingSelected=Der Monat muss ausgewählt sein. birthdayButler.organization.noMatchingUser=Es gibt keine Benutzer:innen, die der in projectforge.properties gesetzten Organisation zugeordnet sind. birthdayButler.organization.notSet=Es ist keine Organisation in den application.properties definiert. -> projectforge.birthdayTool.organization=Your Organization -birthdayButler.wordDocument.error=Es ist ein Fehler beim Erstellen des Word-Dokuments aufgetreten. +birthdayButler.wordDocument.error=Es ist ein Fehler beim Erstellen des Word-Dokuments aufgetreten. \ No newline at end of file diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt index 2bff5443d4..d70c48629c 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt @@ -287,15 +287,12 @@ class AddressViewPageRest : AbstractDynamicPageRest() { if (email.isNullOrBlank()) { return } - val emailObject = EMail(email) col.add( UIRow() .add(UICol(6).add(UILabel(title))) - .add(UICol(6).add(UICustomized("email", mutableMapOf("email" to emailObject)))) + .add(UICol(6).add(UICustomized("email", mutableMapOf("data" to EMail(email))))) ) } - - private fun createAddressCol( row: UIRow, numberOfAddresses: Int, diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt index 1e489d8de8..c08ef48307 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt @@ -33,7 +33,6 @@ import org.projectforge.business.poll.filter.PollAssignmentFilter import org.projectforge.business.poll.filter.PollState import org.projectforge.business.poll.filter.PollStateFilter import org.projectforge.business.user.service.UserService -import org.projectforge.common.i18n.UserException import org.projectforge.framework.access.AccessException import org.projectforge.framework.i18n.translate import org.projectforge.framework.i18n.translateMsg @@ -688,7 +687,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. val dto = postData.data val ptype = dto.prequestionType?.let { PreType.valueOf(it) } ?: PreType.Neujahrsfeier - val question = PQuestion(uid = UUID.randomUUID().toString(), pType = ptype) + val question = PreQuestion(uid = UUID.randomUUID().toString(), pType = ptype) if (ptype == PreType.Sommerfest) { Sommerfest.entries.forEach { entry -> dto.inputFields?.add(entry.value) @@ -902,7 +901,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. ) } else { fieldset - .add(UIReadOnlyField(value = pollDto.title, label = "titel", dataType = UIDataType.STRING)) + .add(UIReadOnlyField(value = pollDto.title, label = "title", dataType = UIDataType.STRING)) .add(UIReadOnlyField(value = pollDto.description, label = "description", dataType = UIDataType.STRING)) .add(UIReadOnlyField(value = pollDto.location, label = "location", dataType = UIDataType.STRING)) .add( diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PQuestion.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PreQuestion.kt similarity index 79% rename from projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PQuestion.kt rename to projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PreQuestion.kt index 31c8ff5dbb..a7c3b91ade 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PQuestion.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/types/PreQuestion.kt @@ -2,7 +2,7 @@ package org.projectforge.rest.poll.types import com.fasterxml.jackson.databind.ObjectMapper -class PQuestion ( +class PreQuestion ( val uid: String? = null, val question: String? = "", val pType: PreType? = PreType.Neujahrsfeier, @@ -11,8 +11,8 @@ class PQuestion ( var isRequired: Boolean? = false, var numberOfSelect: Int? = 1, ) { - fun toObject(string: String): PQuestion { - return ObjectMapper().readValue(string, PQuestion::class.java) + fun toObject(string: String): PreQuestion { + return ObjectMapper().readValue(string, PreQuestion::class.java) } fun toJson(): String { From 1688ab5122eb2a2452338c28b42d3f484c79fb65 Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Wed, 29 May 2024 12:27:21 +0200 Subject: [PATCH 05/16] ExcelExport IndexOutOfBoundFix --- .../rest/poll/excel/ExcelExport.kt | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt index a614283153..40159b2504 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt @@ -78,44 +78,44 @@ class ExcelExport { createNewRow(excelSheet, emptyRow, anzNewRows) setFirstRow(excelSheet, style, poll) + if (responses.isNotEmpty()) { + poll.attendees?.sortedBy { it.displayName } + poll.attendees?.forEachIndexed { index, user -> + val res = PollResponse() + responses.find { it.owner?.id == user.id }?.let { res.copyFrom(it) } + setNewRows(excelSheet, poll, user, res, index + FIRST_DATA_ROW_NUM) + } + val fullAccessUser = poll.fullAccessUsers?.toMutableList() ?: mutableListOf() + val accessGroupIds = poll.fullAccessGroups?.filter { it.id != null }?.map { it.id!! }?.toIntArray() + val accessUserIds = UserService().getUserIds(groupService.getGroupUsers(accessGroupIds)) + val accessUsers = User.toUserList(accessUserIds) + User.restoreDisplayNames(accessUsers, userService) - poll.attendees?.sortedBy { it.displayName } - poll.attendees?.forEachIndexed { index, user -> - val res = PollResponse() - responses.find { it.owner?.id == user.id }?.let { res.copyFrom(it) } - setNewRows(excelSheet, poll, user, res, index + FIRST_DATA_ROW_NUM) - } - - val fullAccessUser = poll.fullAccessUsers?.toMutableList() ?: mutableListOf() - val accessGroupIds = poll.fullAccessGroups?.filter { it.id != null }?.map { it.id!! }?.toIntArray() - val accessUserIds = UserService().getUserIds(groupService.getGroupUsers(accessGroupIds)) - val accessUsers = User.toUserList(accessUserIds) - User.restoreDisplayNames(accessUsers, userService) - - accessUsers?.forEach { user -> - if (fullAccessUser.none { it.id == user.id }) { - fullAccessUser.add(user) + accessUsers?.forEach { user -> + if (fullAccessUser.none { it.id == user.id }) { + fullAccessUser.add(user) + } } - } - var owner = User.getUser(poll.owner?.id, false) - if (owner != null) { - fullAccessUser.add(owner) - } + var owner = User.getUser(poll.owner?.id, false) + if (owner != null) { + fullAccessUser.add(owner) + } - User.restoreDisplayNames(fullAccessUser, userService) - fullAccessUser.forEachIndexed { _, user -> - var number = (anzNewRows) - if (poll.attendees?.map { it.id }?.contains(user.id) == false) { - val res = PollResponse() - responses.find { it.owner?.id == user.id }?.let { res.copyFrom(it) } - // User add a Response - if (res.id != null) { - // Put data in the Row - setNewRows(excelSheet, poll, user, res, number) + User.restoreDisplayNames(fullAccessUser, userService) + fullAccessUser.forEachIndexed { _, user -> + var number = (anzNewRows) + if (poll.attendees?.map { it.id }?.contains(user.id) == false) { + val res = PollResponse() + responses.find { it.owner?.id == user.id }?.let { res.copyFrom(it) } + if (res.id != null) { + setNewRows(excelSheet, poll, user, res, number) + } } } + } else { + println("Keine Antworten gefunden.") } return returnByteFile(excelSheet) @@ -129,6 +129,7 @@ class ExcelExport { } + private fun setFirstRow(excelSheet: ExcelSheet, style: CellStyle, poll: Poll) { val excelRow = excelSheet.getRow(0) val excelRow1 = excelSheet.getRow(1) @@ -194,7 +195,9 @@ class ExcelExport { if (question.type == BaseType.MultiResponseQuestion || question.type == BaseType.SingleResponseQuestion) { if (index == ind && questionpossibilities != null) { excelSheet.autosize(cell) - excelRow.getCell(cell).setCellValue(questionpossibilities.annotation!!.get(0)) + if (questionpossibilities.annotation != null && questionpossibilities.annotation!!.size != 0) { + excelRow.getCell(cell).setCellValue(questionpossibilities.annotation!![0]) + } } } From 49118fddca69c7576188c6d4643e6c080fe71eb1 Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Wed, 29 May 2024 13:30:07 +0200 Subject: [PATCH 06/16] Pull Request fixes --- .../org/projectforge/rest/poll/excel/ExcelExport.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt index 40159b2504..415bf68a18 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt @@ -185,18 +185,18 @@ class ExcelExport { poll.inputFields?.forEachIndexed { _, question -> val questionpossibilities = res?.responses?.find { it.questionUid == question.uid } - var index = 0 - var size = 0; - question.answers?.forEachIndexed { ind, answer -> - index = question.answers!!.size - 1 - cell++ + var index = 0 + var size = 0; + question.answers?.forEachIndexed { ind, answer -> + index = question.answers!!.size - 1 + cell++ if (question.type == BaseType.MultiResponseQuestion || question.type == BaseType.SingleResponseQuestion) { if (index == ind && questionpossibilities != null) { excelSheet.autosize(cell) if (questionpossibilities.annotation != null && questionpossibilities.annotation!!.size != 0) { - excelRow.getCell(cell).setCellValue(questionpossibilities.annotation!![0]) + excelRow.getCell(cell).setCellValue(questionpossibilities.annotation?.get(0)) } } } From 7c9d45b14bbc4547ffb11d7c3afce9f01f2b28f3 Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Wed, 29 May 2024 13:58:55 +0200 Subject: [PATCH 07/16] Excel Export fix --- .../org/projectforge/rest/poll/excel/ExcelExport.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt index 415bf68a18..4e4b18dd39 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt @@ -98,14 +98,14 @@ class ExcelExport { } } - var owner = User.getUser(poll.owner?.id, false) + val owner = User.getUser(poll.owner?.id, false) if (owner != null) { fullAccessUser.add(owner) } User.restoreDisplayNames(fullAccessUser, userService) fullAccessUser.forEachIndexed { _, user -> - var number = (anzNewRows) + val number = (anzNewRows) if (poll.attendees?.map { it.id }?.contains(user.id) == false) { val res = PollResponse() responses.find { it.owner?.id == user.id }?.let { res.copyFrom(it) } @@ -140,8 +140,6 @@ class ExcelExport { var merge = 1 poll.inputFields?.forEach { question -> val answers = question.answers - var totalAnswerLength = question.answers?.sumBy { countWords(it) } ?: 0 - var questionLength = question.question?.let { countWords(it) } if (question.type == BaseType.MultiResponseQuestion || question.type == BaseType.SingleResponseQuestion) { if (!question.answers!!.contains("Anmerkung")) { val ind = question.answers!!.size @@ -186,12 +184,10 @@ class ExcelExport { val questionpossibilities = res?.responses?.find { it.questionUid == question.uid } var index = 0 - var size = 0; question.answers?.forEachIndexed { ind, answer -> index = question.answers!!.size - 1 cell++ - if (question.type == BaseType.MultiResponseQuestion || question.type == BaseType.SingleResponseQuestion) { if (index == ind && questionpossibilities != null) { excelSheet.autosize(cell) From a79b83bcc4163adaeb92811af54019c2b477de6b Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Wed, 29 May 2024 14:14:03 +0200 Subject: [PATCH 08/16] Excel Export fix --- .../org/projectforge/rest/poll/excel/ExcelExport.kt | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt index 4e4b18dd39..cc4059a1a2 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt @@ -183,7 +183,7 @@ class ExcelExport { poll.inputFields?.forEachIndexed { _, question -> val questionpossibilities = res?.responses?.find { it.questionUid == question.uid } - var index = 0 + var index: Int question.answers?.forEachIndexed { ind, answer -> index = question.answers!!.size - 1 cell++ @@ -206,15 +206,15 @@ class ExcelExport { } } else if (question.type == BaseType.SingleResponseQuestion) { excelSheet.autosize(cell) - if (answer is String && answer.equals(questionpossibilities?.answers?.get(0)) && ind != index) { + if (answer == questionpossibilities?.answers?.get(0) && ind != index) { excelRow.getCell(cell).setCellValue("X") } } else { if (questionpossibilities?.answers?.isNotEmpty() == true) { excelSheet.autosize(cell) excelRow.getCell(cell).setCellValue(questionpossibilities.answers?.get(0).toString()) - if (countLines(answer.toString()) > countLines(largestAnswer)) { - largestAnswer = answer.toString() + if (countLines(answer) > countLines(largestAnswer)) { + largestAnswer = answer } } } @@ -236,11 +236,6 @@ class ExcelExport { excelRow.setHeight((14 + counterOfOverlength * 14 + counterOfBreaking * 14).toFloat()) } - - private fun countWords(text: String): Int { - return text.split("\\s+".toRegex()).filter { it.isNotEmpty() }.size - } - private fun countLines(str: String): Int { val lines = str.split("\r\n|\r|\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() From ed2bb05b590c0a99b14659297a67c95027e627ee Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Wed, 29 May 2024 15:01:11 +0200 Subject: [PATCH 09/16] Excel Export fix --- .../projectforge/rest/poll/PollMailService.kt | 21 ++++++------------- .../projectforge/rest/poll/PollPageRest.kt | 18 ++++++---------- .../rest/poll/PollResponsePageRest.kt | 1 - 3 files changed, 12 insertions(+), 28 deletions(-) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt index 9d8a1728a9..463ecd13c8 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollMailService.kt @@ -24,9 +24,6 @@ package org.projectforge.rest.poll import org.projectforge.business.group.service.GroupService -import org.projectforge.business.poll.PollDO -import org.projectforge.business.poll.PollDao -import org.projectforge.business.poll.PollResponseDao import org.projectforge.business.user.service.UserService import org.projectforge.mail.Mail import org.projectforge.mail.MailAttachment @@ -49,12 +46,6 @@ class PollMailService { @Autowired private lateinit var userService: UserService - @Autowired - private lateinit var pollDao: PollDao - - @Autowired - private lateinit var pollResponseDao: PollResponseDao - private val log: Logger = LoggerFactory.getLogger(PollMailService::class.java) fun sendMail( @@ -84,19 +75,19 @@ class PollMailService { fun getAllMails(poll: Poll): List { val attendees = poll.attendees - var fullAccessUser = poll.fullAccessUsers?.toMutableList() ?: mutableListOf() + val fullAccessUser = poll.fullAccessUsers?.toMutableList() ?: mutableListOf() val accessGroupIds = poll.fullAccessGroups?.filter { it.id != null }?.map { it.id!! }?.toIntArray() val accessUserIds = UserService().getUserIds(groupService.getGroupUsers(accessGroupIds)) val accessUsers = User.toUserList(accessUserIds) - var userList = fullAccessUser + val userList = fullAccessUser accessUsers?.forEach { user -> if (fullAccessUser.none { it.id == user.id }) { userList.add(user) } } - var owner = User.getUser(poll.owner?.id, false) + val owner = User.getUser(poll.owner?.id, false) if (owner != null) { userList.add(owner) } @@ -113,9 +104,9 @@ class PollMailService { fun getAllFullAccessEmails(poll: Poll): List { val fullAccessUser = poll.fullAccessUsers?.toMutableList() ?: mutableListOf() - val accessGroupIds = poll.fullAccessGroups?.filter { it.id != null }?.map { it.id!! }?.toIntArray() + poll.fullAccessGroups?.filter { it.id != null }?.map { it.id!! }?.toIntArray() - var userList = fullAccessUser + val userList = fullAccessUser User.restoreEmails(userList, userService) return userList.mapNotNull { it.email } @@ -124,7 +115,7 @@ class PollMailService { fun getAllAttendesEmails(poll: Poll): List { val attendees = poll.attendees - var userList = attendees + val userList = attendees User.restoreEmails(userList, userService) return userList!!.mapNotNull { it.email } diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt index c08ef48307..0ed50a3cf3 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt @@ -88,9 +88,6 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. @Autowired private lateinit var excelExport: ExcelExport - @Autowired - private lateinit var exporter: ExcelExport - @Autowired private lateinit var pollResponseDao: PollResponseDao @@ -215,7 +212,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. addDefaultParameterFields(dto, fieldset, isRunning = dto.state == PollDO.State.RUNNING) - val rowWidth = UILength(xs = 12, sm = 12, md = 12, lg = 12) + UILength(xs = 12, sm = 12, md = 12, lg = 12) val colWidth = UILength(xs = 12, sm = 12, md = 6, lg = 6) fieldset @@ -445,14 +442,13 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. @PutMapping("/finish") fun changeStateToFinish( - request: HttpServletRequest, obj: PollDO, + request: HttpServletRequest, @RequestBody postData: PostData, ): ResponseEntity { postData.data.state = PollDO.State.FINISHED postData.data.deadline = LocalDate.now() val responseEntity = super.saveOrUpdate(request, postData) - var emailSent: Boolean = false - var FUemailSent: Boolean = false + val fuemailSent = false if (postData.data.state == PollDO.State.FINISHED) { @@ -477,7 +473,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. } } - if (FUemailSent != true) { + if (fuemailSent != true) { val owner = userService.getUser(postData.data.owner?.id) val mailFrom = owner?.email.toString() @@ -486,7 +482,6 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. val mailContent = translateMsg("poll.mail.ended.fullAccess.content", postData.data.title, owner?.displayName) pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent, listOf(mailAttachment)) - FUemailSent = true } val owner = userService.getUser(postData.data.owner?.id) @@ -495,7 +490,6 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. val mailSubject = translateMsg("poll.mail.ended.subject", postData.data.title) val mailContent = translateMsg("poll.mail.ended.content", postData.data.title, owner?.displayName) pollMailService.sendMail(mailFrom, mailTo, mailSubject, mailContent) - emailSent = true } return responseEntity } @@ -626,7 +620,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. ): ResponseEntity { val dto = postData.data - val type = dto.questionType?.let { BaseType.valueOf(it) } ?: BaseType.TextQuestion; + val type = dto.questionType?.let { BaseType.valueOf(it) } ?: BaseType.TextQuestion val question = Question(uid = UUID.randomUUID().toString(), type = type) if (type == BaseType.SingleResponseQuestion) { question.answers = mutableListOf("", "") @@ -687,7 +681,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. val dto = postData.data val ptype = dto.prequestionType?.let { PreType.valueOf(it) } ?: PreType.Neujahrsfeier - val question = PreQuestion(uid = UUID.randomUUID().toString(), pType = ptype) + PreQuestion(uid = UUID.randomUUID().toString(), pType = ptype) if (ptype == PreType.Sommerfest) { Sommerfest.entries.forEach { entry -> dto.inputFields?.add(entry.value) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt index 35ef5a4ba1..87b15e930b 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollResponsePageRest.kt @@ -192,7 +192,6 @@ class PollResponsePageRest : AbstractDynamicPageRest() { ) } - var index3 = 0 if (field.type == BaseType.MultiResponseQuestion || field.type === BaseType.SingleResponseQuestion) { field.answers?.forEachIndexed { index2, _ -> if (pollResponse.responses?.get(index)?.answers?.getOrNull(index2) == null) { From 9261835d4ef69d03d09cb644c205ab3a523a7764 Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Wed, 29 May 2024 15:15:30 +0200 Subject: [PATCH 10/16] Excel Export fix --- .../main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt index cc4059a1a2..d6c6330d50 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt @@ -228,7 +228,6 @@ class ExcelExport { val pufferSplit: Array = puffer.split("\r\n|\r|\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() - // check for line-breaks for (i in pufferSplit.indices) { counterOfBreaking++ counterOfOverlength += pufferSplit[i].length / 20 From 8381106bd6ed1cf3e99b1edd491d6643be8081de Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Mon, 3 Jun 2024 10:14:37 +0200 Subject: [PATCH 11/16] Test fail --- .../main/resources/I18nResources.properties | 13 +++-- .../resources/I18nResources_de.properties | 10 +++- .../rest/poll/excel/ExcelExport.kt | 48 +++++++++---------- 3 files changed, 41 insertions(+), 30 deletions(-) diff --git a/projectforge-business/src/main/resources/I18nResources.properties b/projectforge-business/src/main/resources/I18nResources.properties index 6acc28ddee..4fa1f253f5 100644 --- a/projectforge-business/src/main/resources/I18nResources.properties +++ b/projectforge-business/src/main/resources/I18nResources.properties @@ -13,12 +13,15 @@ validation.error.range.integerOutOfRange=Value out of range {0}-{1}. validation.error.range.integerToHigh=Value must not be higher than {0}. validation.error.range.integerToLow=Value must not be lower than {0}. validation.required.valueNotPresent=Value ''{0}'' not present. + # React UI select.placeholder=Select... table.showing=Showing + # own validations: bicvalidator.wronglength=The field ''${label}'' must be 8 or 11 characters long. ibanvalidator.wronglength.de=The field ''${label}'' starts with ''DE'', but a german IBAN must have 22 characters. + # Currency format currencyConverter.percentage.help=You can enter amounts as well as percent values (e. g. 10%). currencyFormat={0,number,,##0.00} @@ -31,6 +34,7 @@ menu.projectDocumentation=Project docs menu.sqlConsole=SQL console menu.userGuide=Handbuch message.notYetImplemented=Not yet implemented. + # Buttons: add=Add assign=Assign @@ -93,6 +97,7 @@ updateAndNext=Update and next upload=Upload uptodate=up-to-date wizard=Wizard + # Common location=Location no=No @@ -1397,7 +1402,7 @@ history.was=was hr.planning.description=Activity report hr.planning.entry.copyFromPredecessor=Copy from predecessor hr.planning.entry.error.entryDoesAlreadyExistForUserAndWeekOfYear=An entry with the same user and week of year does already exist. -hr.planning.entry.error.noRightForProject=You don''t have permissions to create or edit entries for the project ''{0}''. +hr.planning.entry.error.noRightForProject=You don''t have permissions to create or edit entries for the project ''{0}''. hr.planning.entry.error.statusAndProjektNotAllowed=Status and project not allowed. hr.planning.entry.error.statusOrProjektRequired=Status or project required. hr.planning.entry.status.absence=absence @@ -1828,7 +1833,7 @@ plugins.teamcal.event.changedby=Changed by plugins.teamcal.event.convert2Timesheet=Convert to time-sheet plugins.teamcal.event.decline=decline plugins.teamcal.event.deletedby=Deleted by -plugins.teamcal.event.duplicatedUidFromDifferentUser=An event from a another user with UID {0} already exists in calendar {1}. +plugins.teamcal.event.duplicatedUidFromDifferentUser=An event from a another user with UID {0} already exists in calendar {1}. plugins.teamcal.event.duration=Duration plugins.teamcal.event.duration.error=Duration error. Please check start and end date. plugins.teamcal.event.endDate=End @@ -1991,7 +1996,7 @@ poll.confirmation.deleteButton=Yes poll.confirmation.finish=Do you really want to finish this Poll? poll.date=Date poll.deadline=Deadline -poll.delegationAnswers=Answers of +poll.delegationAnswers=Answers of poll.description=Description poll.error.oneQuestionRequired=At least one question is required. poll.exception.noAttendee=This user is not part of the poll. @@ -2373,7 +2378,7 @@ timesheet.stopTime=Stop time timesheet.tag=Tag timesheet.taskReference=Reference of element timesheet.templates=Templates -timesheet.templates.migrationOfLegacy.button=Old templates +timesheet.templates.migrationOfLegacy.button=Old templates timesheet.templates.migrationOfLegacy.confirmationMessage=Do you want to import your old templates from the classical version now? Any existing entry will not be overwritten. timesheet.templates.migrationOfLegacy.tooltip=Imports old templates of classical version. timesheet.templates.new=New template diff --git a/projectforge-business/src/main/resources/I18nResources_de.properties b/projectforge-business/src/main/resources/I18nResources_de.properties index e687deafcd..0d66ae186a 100644 --- a/projectforge-business/src/main/resources/I18nResources_de.properties +++ b/projectforge-business/src/main/resources/I18nResources_de.properties @@ -90,6 +90,7 @@ # system.pluginAdmin.button.deactivate=Deactivate # system.pluginAdmin.title=Plugins # system.statistics.databasePool=Data base pool + # Wicket: datatable.no-records-found=Keine Einträge gefunden. NavigatorLabel=Zeile ${from} bis ${to} von ${of}. @@ -105,12 +106,15 @@ validation.error.range.integerOutOfRange=Wert au validation.error.range.integerToHigh=Wert darf nicht größer als {0} sein. validation.error.range.integerToLow=Wert darf nicht kleiner als {0} sein. validation.required.valueNotPresent=Wert ''{0}'' nicht gegeben. + # React UI select.placeholder=Auswählen... table.showing=Anzeige + # own validations: bicvalidator.wronglength=Das Feld ''${label}'' muss 8 oder 11 Zeichen lang sein. ibanvalidator.wronglength.de=Das Feld ''${label}'' beginnt mit ''DE''. Eine deutsche IBAN muss jedoch aus 22 Zeichen bestehen. + # Currency format currencyConverter.percentage.help=Es können sowohl Beträge als auch Prozentzahlen (z. B. 10%) eingegeben werden. currencyFormat={0,number,,##0.00} @@ -188,6 +192,7 @@ wizard=Assistent State.running=Laufend poll.state.running running=Laufend + # Common akquise=Akquise changes=Änderungen @@ -2061,6 +2066,7 @@ plugins.teamcal.title.add=Kalender hinzuf plugins.teamcal.title.edit=Team-Kalender bearbeiten plugins.teamcal.title.heading=Kalender plugins.teamcal.title.list=Kalenderliste + # poll plugin poll=Umfrage poll.access=Zugriff @@ -2117,7 +2123,7 @@ poll.templateTypeNeujahrsfeier=Neujahrsfeier poll.templateTypeSommerfest=Sommerfest poll.templateTypeTeamessen=Teamessen #Umfrage wurde erstellt -poll.mail.created.subject=

Sie wurden zu einer Umfrage eingeladen mit dem Titel "{0}" eingeladen.

+poll.mail.created.subject=

Sie wurden zu einer Umfrage eingeladen mit dem Titel "{0}" eingeladen.

poll.mail.created.content=

Liebe Teilnehmerinnen und Teilnehmer,

\

Wir möchten Ihnen mitteilen, dass eine Umfrage erstellt wurde mit dem Titel "{0}", und Sie wurden herzlichst eingeladen bei dieser Abzustimmen.

\

Hier kommst du direkt zur Umfrage

\ @@ -2185,7 +2191,7 @@ February=Februar March=März April=April May=Mai -June=Juni +June=Juni July=Juli August=August September=September diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt index d6c6330d50..f972fd3129 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/excel/ExcelExport.kt @@ -188,38 +188,38 @@ class ExcelExport { index = question.answers!!.size - 1 cell++ - if (question.type == BaseType.MultiResponseQuestion || question.type == BaseType.SingleResponseQuestion) { - if (index == ind && questionpossibilities != null) { - excelSheet.autosize(cell) - if (questionpossibilities.annotation != null && questionpossibilities.annotation!!.size != 0) { - excelRow.getCell(cell).setCellValue(questionpossibilities.annotation?.get(0)) - } + if (question.type == BaseType.MultiResponseQuestion || question.type == BaseType.SingleResponseQuestion) { + if (index == ind && questionpossibilities != null) { + excelSheet.autosize(cell) + if (questionpossibilities.annotation != null && questionpossibilities.annotation!!.size != 0) { + excelRow.getCell(cell).setCellValue(questionpossibilities.annotation?.get(0)) } } + } - if (question.type == BaseType.MultiResponseQuestion) { - questionpossibilities?.answers?.forEach { - excelSheet.autosize(cell) - if (questionpossibilities.answers?.get(ind)!!.equals(true) && ind != index) { - excelRow.getCell(cell).setCellValue("X") - } - } - } else if (question.type == BaseType.SingleResponseQuestion) { + if (question.type == BaseType.MultiResponseQuestion) { + questionpossibilities?.answers?.forEach { excelSheet.autosize(cell) - if (answer == questionpossibilities?.answers?.get(0) && ind != index) { + if (questionpossibilities.answers?.get(ind)!!.equals(true) && ind != index) { excelRow.getCell(cell).setCellValue("X") } - } else { - if (questionpossibilities?.answers?.isNotEmpty() == true) { - excelSheet.autosize(cell) - excelRow.getCell(cell).setCellValue(questionpossibilities.answers?.get(0).toString()) - if (countLines(answer) > countLines(largestAnswer)) { - largestAnswer = answer - } + } + } else if (question.type == BaseType.SingleResponseQuestion) { + excelSheet.autosize(cell) + if (answer == questionpossibilities?.answers?.get(0) && ind != index) { + excelRow.getCell(cell).setCellValue("X") + } + } else { + if (questionpossibilities?.answers?.isNotEmpty() == true) { + excelSheet.autosize(cell) + excelRow.getCell(cell).setCellValue(questionpossibilities.answers?.get(0).toString()) + if (countLines(answer) > countLines(largestAnswer)) { + largestAnswer = answer } } } } + } largestAnswer = "i" val puffer: String = largestAnswer @@ -228,6 +228,7 @@ class ExcelExport { val pufferSplit: Array = puffer.split("\r\n|\r|\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + // check for line-breaks for (i in pufferSplit.indices) { counterOfBreaking++ counterOfOverlength += pufferSplit[i].length / 20 @@ -261,5 +262,4 @@ class ExcelExport { return byteArrayOutputStream.toByteArray() } } -} - +} \ No newline at end of file From be5b9e48cc82b4129edc8d48eda1bb9433fd0f3a Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Tue, 4 Jun 2024 12:56:03 +0200 Subject: [PATCH 12/16] Test fail --- .../plugins/datatransfer/rest/DataTransferPersonalBoxPageRest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/org.projectforge.plugins.datatransfer/src/main/kotlin/org/projectforge/plugins/datatransfer/rest/DataTransferPersonalBoxPageRest.kt b/plugins/org.projectforge.plugins.datatransfer/src/main/kotlin/org/projectforge/plugins/datatransfer/rest/DataTransferPersonalBoxPageRest.kt index 11d5168650..1773efe6d4 100644 --- a/plugins/org.projectforge.plugins.datatransfer/src/main/kotlin/org/projectforge/plugins/datatransfer/rest/DataTransferPersonalBoxPageRest.kt +++ b/plugins/org.projectforge.plugins.datatransfer/src/main/kotlin/org/projectforge/plugins/datatransfer/rest/DataTransferPersonalBoxPageRest.kt @@ -25,6 +25,7 @@ package org.projectforge.plugins.datatransfer.rest import org.projectforge.business.user.UserGroupCache import org.projectforge.business.user.service.UserPrefService +import org.projectforge.framework.i18n.translate import org.projectforge.framework.utils.NumberHelper import org.projectforge.model.rest.RestPaths import org.projectforge.plugins.datatransfer.DataTransferAreaDao From 9346629037300f6220e0fb32a77eeb5f42885528 Mon Sep 17 00:00:00 2001 From: Nico <144223089+Nico0000000@users.noreply.github.com> Date: Wed, 10 Jul 2024 08:24:46 +0200 Subject: [PATCH 13/16] Update CustomerPagesRest.kt --- .../main/kotlin/org/projectforge/rest/fibu/CustomerPagesRest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/fibu/CustomerPagesRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/fibu/CustomerPagesRest.kt index bbccaf7806..b4ff3534ae 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/fibu/CustomerPagesRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/fibu/CustomerPagesRest.kt @@ -31,6 +31,7 @@ import org.projectforge.rest.config.JacksonConfiguration import org.projectforge.rest.config.Rest import org.projectforge.rest.core.AbstractDTOPagesRest import org.projectforge.rest.dto.Customer +import org.projectforge.rest.dto.Konto import org.projectforge.ui.* import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity From bd9bffc2b976e4d8382eefbfb06e15f680d969cb Mon Sep 17 00:00:00 2001 From: Nico <144223089+Nico0000000@users.noreply.github.com> Date: Wed, 10 Jul 2024 08:25:21 +0200 Subject: [PATCH 14/16] Update AddressViewPageRest.kt --- .../src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt index d70c48629c..c6040a2393 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/AddressViewPageRest.kt @@ -293,6 +293,7 @@ class AddressViewPageRest : AbstractDynamicPageRest() { .add(UICol(6).add(UICustomized("email", mutableMapOf("data" to EMail(email))))) ) } + private fun createAddressCol( row: UIRow, numberOfAddresses: Int, From 35ec1811f42b89d1438eaa480141d5d2fa47b7c1 Mon Sep 17 00:00:00 2001 From: Nico <144223089+Nico0000000@users.noreply.github.com> Date: Wed, 10 Jul 2024 08:25:59 +0200 Subject: [PATCH 15/16] Update AddressViewPageRest.kt From 27b563362b2feafc7c216fa2823bd8108e2a01be Mon Sep 17 00:00:00 2001 From: Nico Sinkin Date: Wed, 31 Jul 2024 13:51:26 +0200 Subject: [PATCH 16/16] Email new Line Fixed --- .../main/kotlin/org/projectforge/rest/poll/PollPageRest.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt index 0ed50a3cf3..ba17ed06a1 100644 --- a/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt +++ b/projectforge-rest/src/main/kotlin/org/projectforge/rest/poll/PollPageRest.kt @@ -387,7 +387,7 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. "{1}" dto.customemailcontent = content - val subject = "Sie wurden zu einer Umfrage eingeladen mit dem Titel \"{0}\" eingeladen.

" + val subject = "Sie wurden zu einer Umfrage eingeladen mit dem Titel '{0}' eingeladen." dto.customemailsubject = subject addQuestionFieldset(layout, dto, fieldset) @@ -503,6 +503,8 @@ class PollPageRest : AbstractDTOPagesRest(PollDao::class. throw AccessException("poll.error.oneAttendeRequired") } + postData.data.customemailcontent = postData.data.customemailcontent?.replace("\n", "
") + super.onBeforeSaveOrUpdate(request, obj,postData) }