diff --git a/projectforge-application/src/main/resources/i18nKeys.json b/projectforge-application/src/main/resources/i18nKeys.json index 8cb35c66f1..02ab393339 100644 --- a/projectforge-application/src/main/resources/i18nKeys.json +++ b/projectforge-application/src/main/resources/i18nKeys.json @@ -723,7 +723,7 @@ {"i18nKey":"day","bundleName":"I18nResources","translation":"day(s)","translationDE":"Tag(e)","usedInClasses":["org.projectforge.birthdaybutler.BirthdayButlerService","org.projectforge.business.fibu.MonthlyEmployeeReport","org.projectforge.business.gantt.GanttXUnit","org.projectforge.business.teamcal.event.RecurrenceFrequencyModeTwo","org.projectforge.framework.calendar.DayMonthYearHolder"],"usedInFiles":[]}, {"i18nKey":"days","bundleName":"I18nResources","translation":"days","translationDE":"Tage","usedInClasses":["org.projectforge.framework.calendar.WeekHolder","org.projectforge.framework.i18n.Duration","org.projectforge.framework.i18n.TimeAgo","org.projectforge.plugins.liquidityplanning.LiquidityForecastForm","org.projectforge.rest.calendar.FullCalendarEvent","org.projectforge.rest.fibu.EmployeePagesRest","org.projectforge.statistics.TimesheetDisciplineChartBuilder","org.projectforge.web.fibu.AbstractRechnungEditForm","org.projectforge.web.task.TaskEditForm"],"usedInFiles":[]}, {"i18nKey":"deadline","bundleName":"I18nResources","translation":"Deadline","translationDE":"Frist","usedInClasses":["org.projectforge.business.poll.PollDO","org.projectforge.rest.poll.PollInfoPageRest","org.projectforge.rest.poll.PollPageRest"],"usedInFiles":[]}, - {"i18nKey":"default","bundleName":"I18nResources","translation":"Default","translationDE":"Standard","usedInClasses":["org.apache.batik.util.XMLConstants","org.projectforge.business.teamcal.filter.TeamCalCalendarFilter","org.projectforge.caldav.model.AddressBook","org.projectforge.rest.calendar.CalendarSettingsPageRest","org.projectforge.web.fibu.RechnungEditPage"],"usedInFiles":[]}, + {"i18nKey":"default","bundleName":"I18nResources","translation":"Default","translationDE":"Standard","usedInClasses":["org.apache.batik.util.XMLConstants","org.projectforge.business.teamcal.filter.TeamCalCalendarFilter","org.projectforge.carddav.model.AddressBook","org.projectforge.rest.calendar.CalendarSettingsPageRest","org.projectforge.web.fibu.RechnungEditPage"],"usedInFiles":[]}, {"i18nKey":"delete","bundleName":"I18nResources","translation":"Delete","translationDE":"Löschen","usedInClasses":["org.projectforge.favorites.Favorites","org.projectforge.framework.access.AccessEntryDO","org.projectforge.framework.access.OperationType","org.projectforge.framework.jcr.AttachmentsEventType","org.projectforge.framework.persistence.jpa.PersistenceCallsStats","org.projectforge.model.rest.RestPaths","org.projectforge.plugins.datatransfer.restPublic.DataTransferPublicServicesRest","org.projectforge.plugins.merlin.rest.MerlinVariablePageRest","org.projectforge.rest.AddressPagesRest","org.projectforge.rest.AttachmentPageRest","org.projectforge.rest.AttachmentsServicesRest","org.projectforge.rest.TimesheetFavoritesRest","org.projectforge.rest.fibu.EmployeeValidSinceAttrPageRest","org.projectforge.rest.my2fa.WebAuthnEntryPageRest","org.projectforge.rest.orga.VisitorbookEntryPageRest","org.projectforge.rest.task.TaskFavoritesRest","org.projectforge.ui.LayoutUtils","org.projectforge.ui.UIAttachmentList","org.projectforge.web.fibu.AbstractRechnungEditForm","org.projectforge.web.fibu.AuftragEditForm","org.projectforge.web.fibu.PaymentSchedulePanel","org.projectforge.web.gantt.GanttChartEditTreeTablePanel","org.projectforge.web.humanresources.HRPlanningEditForm","org.projectforge.web.teamcal.dialog.TeamCalFilterDialog","org.projectforge.web.wicket.AbstractEditForm","org.projectforge.web.wicket.autocompletion.PFAutoCompleteTextField","org.projectforge.web.wicket.flowlayout.FileUploadPanel","org.projectforge.web.wicket.flowlayout.ImageUploadPanel"],"usedInFiles":["./projectforge-wicket/src/main/java/org/projectforge/web/fibu/PaymentSchedulePanel.html"]}, {"i18nKey":"deleted","bundleName":"I18nResources","translation":"deleted","translationDE":"gelöscht","usedInClasses":["org.projectforge.business.address.AddressDao","org.projectforge.business.fibu.AuftragsCacheService","org.projectforge.business.fibu.ProjektFilter","org.projectforge.business.fibu.RechnungJdbcService","org.projectforge.business.fibu.kost.KostZuweisungExport","org.projectforge.business.humanresources.HRPlanningEntryDao","org.projectforge.business.teamcal.event.TeamEventDao","org.projectforge.business.timesheet.TimesheetDao","org.projectforge.business.user.UserDao","org.projectforge.business.vacation.repository.VacationDao","org.projectforge.export.DOListExcelExporter","org.projectforge.flyway.dbmigration.V7_0_0_15__AuthenticationToken","org.projectforge.flyway.dbmigration.V7_0_0_6__MigrateEmployeeAndCarryVacationDays","org.projectforge.flyway.dbmigration.V7_4_1_3__ReleaseUserPassword","org.projectforge.framework.access.AccessDao","org.projectforge.framework.persistence.api.BaseDao","org.projectforge.framework.persistence.api.MagicFilterProcessor","org.projectforge.framework.persistence.api.QueryFilter","org.projectforge.framework.persistence.database.DatabaseService","org.projectforge.framework.persistence.entities.AbstractBaseDO","org.projectforge.plugins.todo.ToDoDao","org.projectforge.renderer.custom.MicromataFormatter","org.projectforge.rest.core.AbstractPagesRest","org.projectforge.rest.importer.AbstractImportPageRest","org.projectforge.rest.task.TaskServicesRest","org.projectforge.ui.filter.LayoutListFilterUtils","org.projectforge.web.task.TaskTreeForm","org.projectforge.web.wicket.AbstractListForm"],"usedInFiles":[]}, {"i18nKey":"description","bundleName":"I18nResources","translation":"Description","translationDE":"Beschreibung","usedInClasses":["org.projectforge.business.fibu.EmployeeDO","org.projectforge.business.fibu.KontoDO","org.projectforge.business.fibu.KundeDO","org.projectforge.business.fibu.ProjektDO","org.projectforge.business.fibu.datev.EmployeeSalaryExportDao","org.projectforge.business.fibu.kost.Kost1DO","org.projectforge.business.fibu.kost.Kost2ArtDO","org.projectforge.business.fibu.kost.Kost2DO","org.projectforge.business.fibu.kost.KostZuweisungExport","org.projectforge.business.gantt.GanttChartDao","org.projectforge.business.ldap.GroupDOConverter","org.projectforge.business.ldap.LdapGroupDao","org.projectforge.business.ldap.LdapOrganizationalUnitDao","org.projectforge.business.ldap.LdapPersonDao","org.projectforge.business.ldap.PFUserDOConverter","org.projectforge.business.poll.PollDO","org.projectforge.business.scripting.ScriptDO","org.projectforge.business.task.TaskDO","org.projectforge.business.timesheet.TimesheetDO","org.projectforge.business.vacation.model.LeaveAccountEntryDO","org.projectforge.framework.access.GroupTaskAccessDO","org.projectforge.framework.jcr.Attachment","org.projectforge.framework.jobs.JobHandler","org.projectforge.framework.persistence.database.DatabaseService","org.projectforge.framework.persistence.user.entities.GroupDO","org.projectforge.framework.persistence.user.entities.PFUserDO","org.projectforge.plugins.banking.BankAccountDO","org.projectforge.plugins.banking.BankAccountPagesRest","org.projectforge.plugins.datatransfer.rest.DataTransferAreaPagesRest","org.projectforge.plugins.datatransfer.rest.DataTransferAuditPageRest","org.projectforge.plugins.datatransfer.rest.DataTransferPageRest","org.projectforge.plugins.ihk.IHKExporter","org.projectforge.plugins.merlin.MerlinTemplateDO","org.projectforge.plugins.merlin.MerlinVariableBase","org.projectforge.plugins.merlin.rest.MerlinPagesRest","org.projectforge.plugins.todo.ToDoDO","org.projectforge.plugins.todo.ToDoEditForm","org.projectforge.plugins.todo.ToDoListPage","org.projectforge.plugins.todo.rest.ToDoPagesRest","org.projectforge.renderer.custom.MicromataFormatter","org.projectforge.rest.AddressBookPagesRest","org.projectforge.rest.GroupAccessPagesRest","org.projectforge.rest.GroupPagesRest","org.projectforge.rest.TeamCalPagesRest","org.projectforge.rest.TimesheetMultiSelectedPageRest","org.projectforge.rest.TimesheetPagesRest","org.projectforge.rest.UserPagesRest","org.projectforge.rest.VacationAccountPageRest","org.projectforge.rest.calendar.TimesheetEventsProvider","org.projectforge.rest.fibu.CustomerPagesRest","org.projectforge.rest.fibu.KontoPagesRest","org.projectforge.rest.fibu.ProjectMultiSelectedPageRest","org.projectforge.rest.fibu.ProjectPagesRest","org.projectforge.rest.fibu.kost.Kost1PagesRest","org.projectforge.rest.fibu.kost.Kost2ArtPagesRest","org.projectforge.rest.fibu.kost.Kost2PagesRest","org.projectforge.rest.hr.HRPlanningListPagesRest","org.projectforge.rest.hr.LeaveAccountEntryPagesRest","org.projectforge.rest.poll.PollInfoPageRest","org.projectforge.rest.poll.PollPageRest","org.projectforge.rest.scripting.AbstractScriptExecutePageRest","org.projectforge.rest.scripting.MyScriptPagesRest","org.projectforge.rest.scripting.ScriptPagesRest","org.projectforge.rest.task.TaskPagesRest","org.projectforge.ui.UIAttachmentList","org.projectforge.web.access.AccessEditForm","org.projectforge.web.access.AccessListPage","org.projectforge.web.admin.ConfigurationEditForm","org.projectforge.web.admin.ConfigurationListPage","org.projectforge.web.calendar.TimesheetEventsProvider","org.projectforge.web.fibu.CustomerEditForm","org.projectforge.web.fibu.CustomerListPage","org.projectforge.web.fibu.KontoEditForm","org.projectforge.web.fibu.KontoListPage","org.projectforge.web.fibu.KontoSelectPanel","org.projectforge.web.fibu.Kost1EditForm","org.projectforge.web.fibu.Kost1ListPage","org.projectforge.web.fibu.Kost2ArtEditForm","org.projectforge.web.fibu.Kost2ArtListPage","org.projectforge.web.fibu.Kost2EditForm","org.projectforge.web.fibu.Kost2ListPage","org.projectforge.web.fibu.ProjektEditForm","org.projectforge.web.fibu.ProjektListPage","org.projectforge.web.fibu.ReportObjectivesPanel","org.projectforge.web.humanresources.HRPlanningEditForm","org.projectforge.web.humanresources.HRPlanningListPage","org.projectforge.web.task.TaskEditForm","org.projectforge.web.teamcal.admin.TeamCalEditForm","org.projectforge.web.teamcal.admin.TeamCalListPage","org.projectforge.web.timesheet.TimesheetEditForm","org.projectforge.web.timesheet.TimesheetEditPage","org.projectforge.web.timesheet.TimesheetListPage","org.projectforge.web.user.GroupEditForm","org.projectforge.web.user.GroupListPage","org.projectforge.web.wicket.ErrorForm","org.projectforge.web.wicket.FeedbackForm"],"usedInFiles":["./plugins/org.projectforge.plugins.datatransfer/src/main/resources/mail/dataTransferMail.html","./projectforge-business/src/main/resources/htmlTemplates/teamEventResponse.html","./projectforge-business/src/main/resources/mail/teamEventEmail.html","./projectforge-business/src/main/resources/mail/todoChangeNotification.html","./projectforge-wicket/src/main/java/org/projectforge/web/fibu/ReportObjectivesPanel.html"]}, @@ -1370,7 +1370,7 @@ {"i18nKey":"hr.planning.weekend","bundleName":"I18nResources","translation":"Week-end","translationDE":"Wochenende","usedInClasses":["org.projectforge.business.humanresources.HRPlanningEntryDO","org.projectforge.web.humanresources.HRPlanningListPage"],"usedInFiles":[]}, {"i18nKey":"hr.planning.workdays","bundleName":"I18nResources","translation":"Workdays","translationDE":"Arbeitstage","usedInClasses":[],"usedInFiles":[]}, {"i18nKey":"ibanvalidator.wronglength.de","bundleName":"I18nResources","translation":"The field ''${label}'' starts with ''DE'', but a german IBAN must have 22 characters.","translationDE":"Das Feld ''${label}'' beginnt mit ''DE''. Eine deutsche IBAN muss jedoch aus 22 Zeichen bestehen.","usedInClasses":["org.projectforge.web.common.IbanValidator"],"usedInFiles":[]}, - {"i18nKey":"id","bundleName":"I18nResources","translation":"Id","translationDE":"Id","usedInClasses":["org.apache.batik.util.XMLConstants","org.projectforge.business.address.AddressExport","org.projectforge.business.address.PersonalAddressDao","org.projectforge.business.book.BookDao","org.projectforge.business.fibu.AuftragDao","org.projectforge.business.fibu.AuftragsCacheService","org.projectforge.business.fibu.EingangsrechnungDO","org.projectforge.business.fibu.EingangsrechnungsPositionDO","org.projectforge.business.fibu.EmployeeDO","org.projectforge.business.fibu.EmployeeSalaryDao","org.projectforge.business.fibu.EmployeeServiceSupport","org.projectforge.business.fibu.InvoiceService","org.projectforge.business.fibu.ProjektDO","org.projectforge.business.fibu.RechnungDO","org.projectforge.business.fibu.RechnungDao","org.projectforge.business.fibu.RechnungService","org.projectforge.business.fibu.RechnungsPositionDO","org.projectforge.business.fibu.kost.Kost1DO","org.projectforge.business.fibu.kost.Kost1Dao","org.projectforge.business.fibu.kost.Kost2ArtDao","org.projectforge.business.fibu.kost.Kost2DO","org.projectforge.business.fibu.kost.Kost2Dao","org.projectforge.business.fibu.kost.KostZuweisungDO","org.projectforge.business.gantt.GanttChart","org.projectforge.business.gantt.GanttChartDao","org.projectforge.business.gantt.GanttTaskImpl","org.projectforge.business.humanresources.HRPlanningDO","org.projectforge.business.humanresources.HRPlanningDao","org.projectforge.business.humanresources.HRPlanningEntryDO","org.projectforge.business.orga.ContractDao","org.projectforge.business.orga.VisitorbookEntryDO","org.projectforge.business.task.TaskDao","org.projectforge.business.task.TaskNode","org.projectforge.business.task.formatter.WicketTaskFormatter","org.projectforge.business.timesheet.TimesheetDao","org.projectforge.business.timesheet.TimesheetExport","org.projectforge.business.user.GroupDao","org.projectforge.business.user.UserDao","org.projectforge.business.user.UserPrefDao","org.projectforge.business.vacation.model.VacationDO","org.projectforge.excel.ExcelUtils","org.projectforge.framework.ToStringUtil","org.projectforge.framework.access.AccessDao","org.projectforge.framework.access.AccessEntryDO","org.projectforge.framework.access.GroupTaskAccessDO","org.projectforge.framework.jobs.AbstractJob","org.projectforge.framework.json.HibernateProxySerializer","org.projectforge.framework.persistence.api.BaseDao","org.projectforge.framework.persistence.candh.CandHMaster","org.projectforge.framework.persistence.database.DatabaseService","org.projectforge.framework.persistence.database.ReindexerRegistry","org.projectforge.framework.persistence.database.ReindexerStrategy","org.projectforge.framework.persistence.entities.DefaultBaseDO","org.projectforge.framework.persistence.jpa.PfPersistenceContext","org.projectforge.framework.persistence.search.HibernateSearchDependentObjectsReindexer","org.projectforge.framework.persistence.search.HibernateSearchReindexer","org.projectforge.framework.persistence.user.entities.PFUserDO","org.projectforge.framework.persistence.user.entities.UserPrefEntryDO","org.projectforge.framework.persistence.user.entities.UserRightDO","org.projectforge.framework.persistence.xstream.ProxyIdRefMarshaller","org.projectforge.menu.builder.FavoritesMenuReaderWriter","org.projectforge.plugins.banking.BankingServicesRest","org.projectforge.plugins.datatransfer.DataTransferAreaDO","org.projectforge.plugins.datatransfer.rest.DataTransferAuditPageRest","org.projectforge.plugins.datatransfer.rest.DataTransferPageRest","org.projectforge.plugins.datatransfer.restPublic.DataTransferPublicAttachmentPageRest","org.projectforge.plugins.datatransfer.restPublic.DataTransferPublicPageRest","org.projectforge.plugins.datatransfer.restPublic.DataTransferPublicServicesRest","org.projectforge.plugins.ihk.IHKExporter","org.projectforge.plugins.memo.MemoDO","org.projectforge.plugins.merlin.MerlinTemplateDO","org.projectforge.plugins.merlin.rest.MerlinExecutionPageRest","org.projectforge.plugins.merlin.rest.MerlinVariablePageRest","org.projectforge.plugins.skillmatrix.SkillEntryDO","org.projectforge.rest.AddressImageServicesRest","org.projectforge.rest.AddressServicesRest","org.projectforge.rest.AddressViewPageRest","org.projectforge.rest.AttachmentPageRest","org.projectforge.rest.AttachmentsServicesRest","org.projectforge.rest.TimesheetFavoritesRest","org.projectforge.rest.TimesheetMultiSelectedPageRest","org.projectforge.rest.TimesheetPagesRest","org.projectforge.rest.VacationAccountPageRest","org.projectforge.rest.admin.LogViewerPageRest","org.projectforge.rest.calendar.CalendarFilterServicesRest","org.projectforge.rest.calendar.CalendarSettingsPageRest","org.projectforge.rest.calendar.TeamEventPagesRest","org.projectforge.rest.config.IdObjectDeserializer","org.projectforge.rest.config.JacksonConfiguration","org.projectforge.rest.core.AbstractPagesRest","org.projectforge.rest.dvelop.DvelopClient","org.projectforge.rest.fibu.EmployeeValidSinceAttrPageRest","org.projectforge.rest.fibu.kost.Kost2ArtPagesRest","org.projectforge.rest.importer.AbstractImportPageRest","org.projectforge.rest.json.UISelectTypeSerializer","org.projectforge.rest.my2fa.My2FAServicesRest","org.projectforge.rest.my2fa.My2FASetupPageRest","org.projectforge.rest.my2fa.WebAuthnEntryPageRest","org.projectforge.rest.orga.VisitorbookEntryPageRest","org.projectforge.rest.poll.PollPageRest","org.projectforge.rest.scripting.MyScriptExecutePageRest","org.projectforge.rest.scripting.ScriptExecutePageRest","org.projectforge.rest.scripting.ScriptPagesRest","org.projectforge.rest.task.TaskFavoritesRest","org.projectforge.rest.task.TaskServicesRest","org.projectforge.security.dto.WebAuthnPublicKeyCredentialCreationOptions","org.projectforge.security.webauthn.WebAuthnEntryDao","org.projectforge.ui.UISelect","org.projectforge.web.OrphanedLinkFilter","org.projectforge.web.fibu.Kost2ArtEditForm","org.projectforge.web.fibu.Kost2ArtListPage","org.projectforge.web.fibu.NewCustomerSelectPanel","org.projectforge.web.fibu.NewProjektSelectPanel","org.projectforge.web.gantt.GanttTreeTableNode","org.projectforge.web.user.NewGroupSelectPanel","org.projectforge.web.wicket.AbstractEditPage","org.projectforge.web.wicket.components.TabPanel"],"usedInFiles":["./projectforge-rest/src/main/kotlin/org/projectforge/rest/json/Deserializers.kt"]}, + {"i18nKey":"id","bundleName":"I18nResources","translation":"Id","translationDE":"Id","usedInClasses":["org.apache.batik.util.XMLConstants","org.projectforge.business.address.AddressExport","org.projectforge.business.address.PersonalAddressDao","org.projectforge.business.book.BookDao","org.projectforge.business.fibu.AuftragDao","org.projectforge.business.fibu.AuftragsCacheService","org.projectforge.business.fibu.EingangsrechnungDO","org.projectforge.business.fibu.EingangsrechnungsPositionDO","org.projectforge.business.fibu.EmployeeDO","org.projectforge.business.fibu.EmployeeSalaryDao","org.projectforge.business.fibu.EmployeeServiceSupport","org.projectforge.business.fibu.InvoiceService","org.projectforge.business.fibu.ProjektDO","org.projectforge.business.fibu.RechnungDO","org.projectforge.business.fibu.RechnungDao","org.projectforge.business.fibu.RechnungService","org.projectforge.business.fibu.RechnungsPositionDO","org.projectforge.business.fibu.kost.Kost1DO","org.projectforge.business.fibu.kost.Kost1Dao","org.projectforge.business.fibu.kost.Kost2ArtDao","org.projectforge.business.fibu.kost.Kost2DO","org.projectforge.business.fibu.kost.Kost2Dao","org.projectforge.business.fibu.kost.KostZuweisungDO","org.projectforge.business.gantt.GanttChart","org.projectforge.business.gantt.GanttChartDao","org.projectforge.business.gantt.GanttTaskImpl","org.projectforge.business.humanresources.HRPlanningDO","org.projectforge.business.humanresources.HRPlanningDao","org.projectforge.business.humanresources.HRPlanningEntryDO","org.projectforge.business.orga.ContractDao","org.projectforge.business.orga.VisitorbookEntryDO","org.projectforge.business.task.TaskDao","org.projectforge.business.task.TaskNode","org.projectforge.business.task.formatter.WicketTaskFormatter","org.projectforge.business.timesheet.TimesheetDao","org.projectforge.business.timesheet.TimesheetExport","org.projectforge.business.user.GroupDao","org.projectforge.business.user.UserDao","org.projectforge.business.user.UserPrefDao","org.projectforge.business.vacation.model.VacationDO","org.projectforge.excel.ExcelUtils","org.projectforge.framework.ToStringUtil","org.projectforge.framework.access.AccessDao","org.projectforge.framework.access.AccessEntryDO","org.projectforge.framework.access.GroupTaskAccessDO","org.projectforge.framework.jobs.AbstractJob","org.projectforge.framework.json.HibernateProxySerializer","org.projectforge.framework.persistence.api.BaseDao","org.projectforge.framework.persistence.candh.CandHMaster","org.projectforge.framework.persistence.database.DatabaseService","org.projectforge.framework.persistence.database.ReindexerRegistry","org.projectforge.framework.persistence.database.ReindexerStrategy","org.projectforge.framework.persistence.entities.DefaultBaseDO","org.projectforge.framework.persistence.jpa.PfPersistenceContext","org.projectforge.framework.persistence.search.HibernateSearchDependentObjectsReindexer","org.projectforge.framework.persistence.user.entities.PFUserDO","org.projectforge.framework.persistence.user.entities.UserPrefEntryDO","org.projectforge.framework.persistence.user.entities.UserRightDO","org.projectforge.framework.persistence.xstream.ProxyIdRefMarshaller","org.projectforge.menu.builder.FavoritesMenuReaderWriter","org.projectforge.plugins.banking.BankingServicesRest","org.projectforge.plugins.datatransfer.DataTransferAreaDO","org.projectforge.plugins.datatransfer.rest.DataTransferAuditPageRest","org.projectforge.plugins.datatransfer.rest.DataTransferPageRest","org.projectforge.plugins.datatransfer.restPublic.DataTransferPublicAttachmentPageRest","org.projectforge.plugins.datatransfer.restPublic.DataTransferPublicPageRest","org.projectforge.plugins.datatransfer.restPublic.DataTransferPublicServicesRest","org.projectforge.plugins.ihk.IHKExporter","org.projectforge.plugins.memo.MemoDO","org.projectforge.plugins.merlin.MerlinTemplateDO","org.projectforge.plugins.merlin.rest.MerlinExecutionPageRest","org.projectforge.plugins.merlin.rest.MerlinVariablePageRest","org.projectforge.plugins.skillmatrix.SkillEntryDO","org.projectforge.rest.AddressImageServicesRest","org.projectforge.rest.AddressServicesRest","org.projectforge.rest.AddressViewPageRest","org.projectforge.rest.AttachmentPageRest","org.projectforge.rest.AttachmentsServicesRest","org.projectforge.rest.TimesheetFavoritesRest","org.projectforge.rest.TimesheetMultiSelectedPageRest","org.projectforge.rest.TimesheetPagesRest","org.projectforge.rest.VacationAccountPageRest","org.projectforge.rest.admin.LogViewerPageRest","org.projectforge.rest.calendar.CalendarFilterServicesRest","org.projectforge.rest.calendar.CalendarSettingsPageRest","org.projectforge.rest.calendar.TeamEventPagesRest","org.projectforge.rest.config.IdObjectDeserializer","org.projectforge.rest.config.JacksonConfiguration","org.projectforge.rest.core.AbstractPagesRest","org.projectforge.rest.dvelop.DvelopClient","org.projectforge.rest.fibu.EmployeeValidSinceAttrPageRest","org.projectforge.rest.fibu.kost.Kost2ArtPagesRest","org.projectforge.rest.importer.AbstractImportPageRest","org.projectforge.rest.json.UISelectTypeSerializer","org.projectforge.rest.my2fa.My2FAServicesRest","org.projectforge.rest.my2fa.My2FASetupPageRest","org.projectforge.rest.my2fa.WebAuthnEntryPageRest","org.projectforge.rest.orga.VisitorbookEntryPageRest","org.projectforge.rest.poll.PollPageRest","org.projectforge.rest.scripting.MyScriptExecutePageRest","org.projectforge.rest.scripting.ScriptExecutePageRest","org.projectforge.rest.scripting.ScriptPagesRest","org.projectforge.rest.task.TaskFavoritesRest","org.projectforge.rest.task.TaskServicesRest","org.projectforge.security.dto.WebAuthnPublicKeyCredentialCreationOptions","org.projectforge.security.webauthn.WebAuthnEntryDao","org.projectforge.ui.UISelect","org.projectforge.web.OrphanedLinkFilter","org.projectforge.web.fibu.Kost2ArtEditForm","org.projectforge.web.fibu.Kost2ArtListPage","org.projectforge.web.fibu.NewCustomerSelectPanel","org.projectforge.web.fibu.NewProjektSelectPanel","org.projectforge.web.gantt.GanttTreeTableNode","org.projectforge.web.user.NewGroupSelectPanel","org.projectforge.web.wicket.AbstractEditPage","org.projectforge.web.wicket.components.TabPanel"],"usedInFiles":["./projectforge-rest/src/main/kotlin/org/projectforge/rest/json/Deserializers.kt"]}, {"i18nKey":"imageFile","bundleName":"I18nResources","translation":"Image","translationDE":"Bild","usedInClasses":[],"usedInFiles":[]}, {"i18nKey":"import","bundleName":"I18nResources","translation":"Import","translationDE":"Importieren","usedInClasses":["org.projectforge.business.scripting.KotlinScriptExecutor","org.projectforge.rest.importer.AbstractImportPageRest","org.projectforge.web.admin.SetupImportForm","org.projectforge.web.fibu.ReportObjectivesForm","org.projectforge.web.teamcal.admin.TeamCalEditPage"],"usedInFiles":["./projectforge-wicket/src/main/java/org/projectforge/web/admin/SetupPage.html"]}, {"i18nKey":"import.confirmMessage","bundleName":"I18nResources","translation":"Would you like to import the selected entries now? This option isn't undoable.","translationDE":"Sollen nun alle ausgewählten Einträge importiert werden? Diese Aktion kann nicht rückgängig gemacht werden.","usedInClasses":["org.projectforge.rest.importer.AbstractImportPageRest"],"usedInFiles":[]}, @@ -2494,7 +2494,7 @@ {"i18nKey":"undelete","bundleName":"I18nResources","translation":"Undelete","translationDE":"Wiederherstellen","usedInClasses":["org.projectforge.framework.access.OperationType","org.projectforge.framework.persistence.api.BaseDOPersistenceService","org.projectforge.model.rest.RestPaths","org.projectforge.ui.LayoutUtils","org.projectforge.ui.UIButton","org.projectforge.web.humanresources.HRPlanningEditForm","org.projectforge.web.wicket.AbstractEditForm"],"usedInFiles":[]}, {"i18nKey":"unkown","bundleName":"I18nResources","translation":"unknown","translationDE":"unbekannt","usedInClasses":["org.projectforge.web.rest.RestAuthenticationInfo"],"usedInFiles":[]}, {"i18nKey":"until","bundleName":"I18nResources","translation":"until","translationDE":"bis","usedInClasses":["org.projectforge.plugins.banking.BankAccountRecordDao","org.projectforge.rest.TimesheetPagesRest","org.projectforge.web.teamcal.event.TeamEventEditForm","org.projectforge.web.timesheet.TimesheetEditForm"],"usedInFiles":[]}, - {"i18nKey":"untitled","bundleName":"I18nResources","translation":"untitled","translationDE":"unbenannt","usedInClasses":["org.projectforge.business.scripting.ScriptExecutor","org.projectforge.caldav.model.Contact","org.projectforge.caldav.service.CalendarService","org.projectforge.rest.my2fa.WebAuthnEntryPageRest","org.projectforge.web.gantt.GanttChartEditForm","org.projectforge.web.gantt.GanttChartEditTreeTablePanel"],"usedInFiles":[]}, + {"i18nKey":"untitled","bundleName":"I18nResources","translation":"untitled","translationDE":"unbenannt","usedInClasses":["org.projectforge.business.scripting.ScriptExecutor","org.projectforge.rest.my2fa.WebAuthnEntryPageRest","org.projectforge.web.gantt.GanttChartEditForm","org.projectforge.web.gantt.GanttChartEditTreeTablePanel"],"usedInFiles":[]}, {"i18nKey":"update","bundleName":"I18nResources","translation":"Update","translationDE":"Ändern","usedInClasses":["org.projectforge.framework.access.AccessEntryDO","org.projectforge.framework.access.OperationType","org.projectforge.framework.persistence.api.BaseDOPersistenceService","org.projectforge.framework.persistence.database.DatabaseService","org.projectforge.framework.persistence.jpa.PersistenceCallsStats","org.projectforge.model.rest.RestPaths","org.projectforge.plugins.merlin.rest.MerlinVariablePageRest","org.projectforge.rest.AttachmentPageRest","org.projectforge.rest.TimesheetMultiSelectedPageRest","org.projectforge.rest.fibu.EmployeeValidSinceAttrPageRest","org.projectforge.rest.my2fa.WebAuthnEntryPageRest","org.projectforge.rest.orga.VisitorbookEntryPageRest","org.projectforge.ui.LayoutUtils","org.projectforge.ui.UIButton","org.projectforge.web.fibu.AbstractRechnungEditForm","org.projectforge.web.wicket.AbstractEditForm"],"usedInFiles":[]}, {"i18nKey":"updateAll","bundleName":"I18nResources","translation":"Update all","translationDE":"Alle ändern","usedInClasses":[],"usedInFiles":[]}, {"i18nKey":"updateAndNext","bundleName":"I18nResources","translation":"Update and next","translationDE":"Ändern und nächster","usedInClasses":["org.projectforge.web.wicket.AbstractEditForm"],"usedInFiles":[]}, diff --git a/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/CardDavController.kt b/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/CardDavController.kt index fef341f9ab..e8e056312f 100644 --- a/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/CardDavController.kt +++ b/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/CardDavController.kt @@ -25,17 +25,25 @@ package org.projectforge.carddav import jakarta.servlet.http.HttpServletRequest import mu.KotlinLogging +import org.projectforge.carddav.model.AddressBook +import org.projectforge.carddav.model.User +import org.projectforge.carddav.service.AddressService +import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* +import java.util.Date private val log = KotlinLogging.logger {} @RestController @RequestMapping("/carddav") class CardDavController { + @Autowired + private lateinit var addressService: AddressService + /** * Handle PROPFIND requests. Get the address book metadata for the given user. * @param user The user for which the address book is requested. @@ -48,10 +56,16 @@ class CardDavController { log.error { "Method not allowed: ${request.method}, PROPFIND expected." } return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).build() } - val response = generatePropfindResponse(user) + val response = StringBuilder() + writeMultiStatusStart(response, request.serverName) + val addressBook = AddressBook(User(user)) + addressService.getContactList(addressBook).forEach { contact -> + writeResponse(response, user, contact.id, contact.lastUpdated, contact.displayName) + } + writeMultiStatusEnd(response) return ResponseEntity.status(HttpStatus.MULTI_STATUS) .contentType(MediaType.APPLICATION_XML) - .body(response) + .body(response.toString()) } @RequestMapping(value = ["/**"], method = [RequestMethod.OPTIONS]) @@ -65,48 +79,16 @@ class CardDavController { @GetMapping("/{user}/addressbook/{contactId}") fun getContact(@PathVariable user: String, @PathVariable contactId: String): ResponseEntity { - val vCard = ""//contactService.getContactAsVCard(user, contactId) + val vcard = contactId.toLongOrNull()?.let { + addressService.getContact(it)?.vcardData?.toString(Charsets.UTF_8) + } + if (vcard == null) { + log.error { "Invalid contact id: $contactId" } + return ResponseEntity.notFound().build() + } return ResponseEntity.ok() .contentType(MediaType.parseMediaType("text/vcard")) - .body(vCard) - } - - private fun generatePropfindResponse(user: String): String { - return """ - - - /carddav/$user/addressbook/contact1.vcf - - - "12345" - John Doe - - HTTP/1.1 200 OK - - - - /carddav/user/addressbook/contact2.vcf - - - "67890" - Jane Doe - - HTTP/1.1 200 OK - - - - - - /carddav/$user/addressbook/ - - HTTP/1.1 200 OK - - $user's Address Book - - - - - """.trimIndent() + .body(vcard) } companion object { @@ -123,21 +105,21 @@ class CardDavController { internal fun writeResponse( sb: StringBuilder, user: String, - addressId: Long, - etag: String, + addressId: Long?, + etag: Date?, displayName: String, ) { sb.appendLine(" ") .append(" /carddav/") .append(user) .append("/addressbook/contact") - .append(addressId) + .append(addressId ?: -1L) .appendLine(".vcf") .appendLine(" ") .appendLine(" ") // According to the HTTP/1.1 specification, which is also used by CardDAV, ETags must be enclosed in double quotes. .append(" \"") - .append(etag) + .append(etag?.time ?: -1) .appendLine("\"") .append(" ") .append(displayName) diff --git a/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/model/Contact.kt b/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/model/Contact.kt index 47cb76966b..cfd8eefce5 100644 --- a/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/model/Contact.kt +++ b/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/model/Contact.kt @@ -30,8 +30,8 @@ data class Contact( val lastUpdated: java.util.Date? = null, var vcardData: ByteArray? = null, ) { - + val displayName = "$lastName, $firstName" override fun toString(): String { - return "Contact[id=$id, name=[$lastName, $firstName], lastUpdated=$lastUpdated, vcardData-size=${vcardData?.size}]" + return "Contact[id=$id, name=[$displayName], lastUpdated=$lastUpdated, vcardData-size=${vcardData?.size}]" } } diff --git a/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/model/User.kt b/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/model/User.kt index 38ce3946cf..16be647c3c 100644 --- a/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/model/User.kt +++ b/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/model/User.kt @@ -23,6 +23,4 @@ package org.projectforge.carddav.model -class User { - var userName: String? = null -} +class User(val userName: String? = null) diff --git a/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/service/AddressDAVCache.kt b/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/service/AddressDAVCache.kt index b1ac4515d6..ecf3c96289 100644 --- a/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/service/AddressDAVCache.kt +++ b/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/service/AddressDAVCache.kt @@ -52,7 +52,11 @@ open class AddressDAVCache : AbstractCache(TICKS_PER_HOUR), BaseDOModifiedListen private var contactMap = mutableMapOf() - open fun getContacts(addressBook: AddressBook, ids: List): List { + fun getContact(id: Long): Contact? { + return getCachedAddress(id) + } + + fun getContacts(addressBook: AddressBook, ids: List): List { val result = mutableListOf() val missedInCache = mutableListOf() ids.forEach { diff --git a/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/service/AddressService.kt b/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/service/AddressService.kt index a01e56f0ec..279bb3ff7c 100644 --- a/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/service/AddressService.kt +++ b/projectforge-caldav/src/main/kotlin/org/projectforge/carddav/service/AddressService.kt @@ -37,7 +37,7 @@ private val log = KotlinLogging.logger {} @Service class AddressService { @Autowired - private lateinit var addressDAVCache: org.projectforge.carddav.service.AddressDAVCache + private lateinit var addressDAVCache: AddressDAVCache @Autowired private lateinit var personalAddressDao: PersonalAddressDao @@ -47,6 +47,15 @@ class AddressService { return addressDAVCache.getContacts(addressBook, favorites) } + fun getContact(id: Long): Contact? { + val favorites = personalAddressDao.favoriteAddressIdList + if (!favorites.contains(id)) { + log.info { "Contact with id=$id is not in favorites. Don't returning contact." } + return null + } + return addressDAVCache.getContact(id) + } + @Suppress("UNUSED_PARAMETER") fun createContact(ab: AddressBook, vcardBytearray: ByteArray): Contact { log.warn("Creation of contacts not supported.")