diff options
46 files changed, 517 insertions, 45 deletions
diff --git a/apps/dav/l10n/el.js b/apps/dav/l10n/el.js index dc5084f506b..45ce51a502f 100644 --- a/apps/dav/l10n/el.js +++ b/apps/dav/l10n/el.js @@ -9,6 +9,7 @@ OC.L10N.register( "You deleted calendar {calendar}" : "Διαγράψατε το ημερολόγιο {calendar}", "{actor} updated calendar {calendar}" : "{actor} ενημέρωσε το ημερολόγιο {calendar}", "You updated calendar {calendar}" : "Έχετε ενημερώσει το ημερολόγιο {calendar}", + "You restored calendar {calendar}" : "Επαναφέρατε το ημερολόγιο {calendar}", "You shared calendar {calendar} as public link" : "Μοιραστήκατε το ημερολόγιο {calendar} με δημόσιο σύνδεσμο.", "You removed public link for calendar {calendar}" : "Αφαιρέσατε τον δημόσιο σύνδεσμο για το ημερολόγιο {calendar}", "{actor} shared calendar {calendar} with you" : "{actor} διαμοιράστηκε το ημερολόγιο {calendar} με εσάς", @@ -64,6 +65,7 @@ OC.L10N.register( "More options …" : "Περισσότερες επιλογές...", "More options at %s" : "Περισσότερες επιλογές στο %s", "Contacts" : "Επαφές", + "System is in maintenance mode." : "Το σύστημα βρίσκεται σε λειτουργία συντήρησης.", "Upgrade needed" : "Απαιτείται αναβάθμιση", "Your %s needs to be configured to use HTTPS in order to use CalDAV and CardDAV with iOS/macOS." : "Το %s θα πρέπει να ρυθμιστεί για να χρησιμοποιεί HTTPS για την χρήση του CalDAV και του CardDAV με το iOS/macOS.", "Configures a CalDAV account" : "Ρυθμίσεις λογαριασμού CalDAV", @@ -75,9 +77,12 @@ OC.L10N.register( "Contacts and groups" : "Επαφές και ομάδες", "WebDAV" : "WebDAV", "WebDAV endpoint" : "Τερματικό WebDAV", + "Availability" : "Διαθεσιμότητα", + "If you configure your working hours, other users will see when you are out of office when they book a meeting." : "Εάν διαμορφώσετε τις ώρες εργασίας σας, οι άλλοι χρήστες θα βλέπουν πότε είστε εκτός γραφείου όταν κάνουν κράτηση για μια συνάντηση.", "Time zone:" : "Ζώνη ώρας:", "to" : "προς", "Delete slot" : "Διαγραφή θέσης", + "No working hours set" : "Δεν έχει οριστεί ωράριο εργασίας", "Monday" : "Δευτέρα", "Tuesday" : "Τρίτη", "Wednesday" : "Τετάρτη", @@ -86,6 +91,8 @@ OC.L10N.register( "Saturday" : "Σάββατο", "Sunday" : "Κυριακή", "Save" : "Αποθήκευση", + "Failed to load availability" : "Αποτυχία φόρτωσης της διαθεσιμότητας", + "Failed to save availability" : "Αποτυχία αποθήκευσης της διαθεσιμότητας", "Calendar server" : "Διακομιστής ημερολογίου", "Send invitations to attendees" : "Αποστολή προσκλήσεων στους συμμετέχοντες.", "Automatically generate a birthday calendar" : "Δημιουργία ημερολογίου γενεθλίων αυτόματα", @@ -93,6 +100,8 @@ OC.L10N.register( "Hence they will not be available immediately after enabling but will show up after some time." : "Ως εκ τούτου, δεν θα είναι διαθέσιμα αμέσως μετά την ενεργοποίηση, αλλά θα εμφανιστούν μετά από λίγη ώρα.", "Send notifications for events" : "Αποστολή ειδοποιήσεων για γεγονότα", "Notifications are sent via background jobs, so these must occur often enough." : "Οι ειδοποιήσεις αποστέλλονται μέσω εργασιών παρασκηνίου, οπότε πρέπει να εμφανίζονται αρκετά συχνά.", + "Send reminder notifications to calendar sharees as well" : "Αποστολή ειδοποιήσεων υπενθύμισης και στους κοινούς χρήστες του ημερολογίου", + "Reminders are always sent to organizers and attendees." : "Οι υπενθυμίσεις αποστέλλονται πάντα στους διοργανωτές και στους συμμετέχοντες.", "Enable notifications for events via push" : "Ενεργοποίηση ειδοποιήσεων μέσω push", "Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Εγκαταστήστε επίσης την {calendarappstoreopen}Εφαρμογή ημερολογίου{linkclose}, ή {calendardocopen}συνδέστε τον υπολογιστή & το κινητό σας για συγχρονισμό ↗{linkclose}.", "Please make sure to properly set up {emailopen}the email server{linkclose}." : "Παρακαλώ σιγουρευτείτε για την σωστή ρύθμιση {emailopen}του διακομιστή αλληλογραφίας{linkclose}.", @@ -100,6 +109,7 @@ OC.L10N.register( "Please contact the organizer directly." : "Παρακαλώ επικοινωνήστε απ' ευθείας με τον διοργανωτή.", "Are you accepting the invitation?" : "Αποδέχεστε την πρόσκληση;", "Tentative" : "Δοκιμαστικό", + "Number of guests" : "Πλήθος επισκεπτών", "Comment" : "Σχόλιο", "Your attendance was updated successfully." : "Η παρουσία σας ενημερώθηκε με επιτυχία.", "Todos" : "Εργασίες προς εκτέλεση", diff --git a/apps/dav/l10n/el.json b/apps/dav/l10n/el.json index d76e8094d4f..019d0e757c9 100644 --- a/apps/dav/l10n/el.json +++ b/apps/dav/l10n/el.json @@ -7,6 +7,7 @@ "You deleted calendar {calendar}" : "Διαγράψατε το ημερολόγιο {calendar}", "{actor} updated calendar {calendar}" : "{actor} ενημέρωσε το ημερολόγιο {calendar}", "You updated calendar {calendar}" : "Έχετε ενημερώσει το ημερολόγιο {calendar}", + "You restored calendar {calendar}" : "Επαναφέρατε το ημερολόγιο {calendar}", "You shared calendar {calendar} as public link" : "Μοιραστήκατε το ημερολόγιο {calendar} με δημόσιο σύνδεσμο.", "You removed public link for calendar {calendar}" : "Αφαιρέσατε τον δημόσιο σύνδεσμο για το ημερολόγιο {calendar}", "{actor} shared calendar {calendar} with you" : "{actor} διαμοιράστηκε το ημερολόγιο {calendar} με εσάς", @@ -62,6 +63,7 @@ "More options …" : "Περισσότερες επιλογές...", "More options at %s" : "Περισσότερες επιλογές στο %s", "Contacts" : "Επαφές", + "System is in maintenance mode." : "Το σύστημα βρίσκεται σε λειτουργία συντήρησης.", "Upgrade needed" : "Απαιτείται αναβάθμιση", "Your %s needs to be configured to use HTTPS in order to use CalDAV and CardDAV with iOS/macOS." : "Το %s θα πρέπει να ρυθμιστεί για να χρησιμοποιεί HTTPS για την χρήση του CalDAV και του CardDAV με το iOS/macOS.", "Configures a CalDAV account" : "Ρυθμίσεις λογαριασμού CalDAV", @@ -73,9 +75,12 @@ "Contacts and groups" : "Επαφές και ομάδες", "WebDAV" : "WebDAV", "WebDAV endpoint" : "Τερματικό WebDAV", + "Availability" : "Διαθεσιμότητα", + "If you configure your working hours, other users will see when you are out of office when they book a meeting." : "Εάν διαμορφώσετε τις ώρες εργασίας σας, οι άλλοι χρήστες θα βλέπουν πότε είστε εκτός γραφείου όταν κάνουν κράτηση για μια συνάντηση.", "Time zone:" : "Ζώνη ώρας:", "to" : "προς", "Delete slot" : "Διαγραφή θέσης", + "No working hours set" : "Δεν έχει οριστεί ωράριο εργασίας", "Monday" : "Δευτέρα", "Tuesday" : "Τρίτη", "Wednesday" : "Τετάρτη", @@ -84,6 +89,8 @@ "Saturday" : "Σάββατο", "Sunday" : "Κυριακή", "Save" : "Αποθήκευση", + "Failed to load availability" : "Αποτυχία φόρτωσης της διαθεσιμότητας", + "Failed to save availability" : "Αποτυχία αποθήκευσης της διαθεσιμότητας", "Calendar server" : "Διακομιστής ημερολογίου", "Send invitations to attendees" : "Αποστολή προσκλήσεων στους συμμετέχοντες.", "Automatically generate a birthday calendar" : "Δημιουργία ημερολογίου γενεθλίων αυτόματα", @@ -91,6 +98,8 @@ "Hence they will not be available immediately after enabling but will show up after some time." : "Ως εκ τούτου, δεν θα είναι διαθέσιμα αμέσως μετά την ενεργοποίηση, αλλά θα εμφανιστούν μετά από λίγη ώρα.", "Send notifications for events" : "Αποστολή ειδοποιήσεων για γεγονότα", "Notifications are sent via background jobs, so these must occur often enough." : "Οι ειδοποιήσεις αποστέλλονται μέσω εργασιών παρασκηνίου, οπότε πρέπει να εμφανίζονται αρκετά συχνά.", + "Send reminder notifications to calendar sharees as well" : "Αποστολή ειδοποιήσεων υπενθύμισης και στους κοινούς χρήστες του ημερολογίου", + "Reminders are always sent to organizers and attendees." : "Οι υπενθυμίσεις αποστέλλονται πάντα στους διοργανωτές και στους συμμετέχοντες.", "Enable notifications for events via push" : "Ενεργοποίηση ειδοποιήσεων μέσω push", "Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Εγκαταστήστε επίσης την {calendarappstoreopen}Εφαρμογή ημερολογίου{linkclose}, ή {calendardocopen}συνδέστε τον υπολογιστή & το κινητό σας για συγχρονισμό ↗{linkclose}.", "Please make sure to properly set up {emailopen}the email server{linkclose}." : "Παρακαλώ σιγουρευτείτε για την σωστή ρύθμιση {emailopen}του διακομιστή αλληλογραφίας{linkclose}.", @@ -98,6 +107,7 @@ "Please contact the organizer directly." : "Παρακαλώ επικοινωνήστε απ' ευθείας με τον διοργανωτή.", "Are you accepting the invitation?" : "Αποδέχεστε την πρόσκληση;", "Tentative" : "Δοκιμαστικό", + "Number of guests" : "Πλήθος επισκεπτών", "Comment" : "Σχόλιο", "Your attendance was updated successfully." : "Η παρουσία σας ενημερώθηκε με επιτυχία.", "Todos" : "Εργασίες προς εκτέλεση", diff --git a/apps/dav/l10n/es.js b/apps/dav/l10n/es.js index dad59cef5eb..e9c0b27177c 100644 --- a/apps/dav/l10n/es.js +++ b/apps/dav/l10n/es.js @@ -2,6 +2,7 @@ OC.L10N.register( "dav", { "Calendar" : "Calendario", + "To-dos" : "Tareas", "Personal" : "Personal", "{actor} created calendar {calendar}" : "{actor} creó el calendario {calendar}", "You created calendar {calendar}" : "Usted creó el calendario {calendar}", @@ -36,9 +37,22 @@ OC.L10N.register( "{actor} restored event {event} of calendar {calendar}" : "{actor} ha restablecido el evento {event} del calendario {calendar}", "You restored event {event} of calendar {calendar}" : "Has reestablecido el evento {event} del calendario {calendar}", "Busy" : "Ocupado", + "{actor} created to-do {todo} in list {calendar}" : "{actor} ha creado la tarea {todo} en la lista {calendar}", + "You created to-do {todo} in list {calendar}" : "Has creado la tarea {todo} en la lista {calendar}", + "{actor} deleted to-do {todo} from list {calendar}" : "{actor} ha eliminado la tarea{todo} de la lista {calendar}", + "You deleted to-do {todo} from list {calendar}" : "Has eliminado la tarea {todo} de la lista {calendar}", + "{actor} updated to-do {todo} in list {calendar}" : "{actor} ha actualizado la tarea {todo} en la lista {calendar}", + "You updated to-do {todo} in list {calendar}" : "Has actualizado la tarea {todo} en la lista {calendar}", + "{actor} solved to-do {todo} in list {calendar}" : "{actor} ha resuelto la tarea {todo} de la lista {calendar}", + "You solved to-do {todo} in list {calendar}" : "Has resuelto la tarea {todo} de la lista {calendar}", + "{actor} reopened to-do {todo} in list {calendar}" : "{actor} ha reabierto la tarea {todo} en la lista {calendar}", + "You reopened to-do {todo} in list {calendar}" : "Has reabierto la tarea {todo} en la lista {calendar}", + "{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{actor} ha movido la tarea {todo} de la lista {sourceCalendar} a la lista{targetCalendar}", + "You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "Has movido la tarea {todo} de la lista {sourceCalendar} a la lista {targetCalendar}", "Calendar, contacts and tasks" : "Calendario, contactos y tareas", "A <strong>calendar</strong> was modified" : "Un <strong>calendario</strong> fue modificado.", "A calendar <strong>event</strong> was modified" : "Un <strong>evento</strong> del calendario fue modificado.", + "A calendar <strong>to-do</strong> was modified" : "Se ha modificado una <strong>tarea</strong> de calendario", "Contact birthdays" : "Cumpleaños del contacto", "Death of %s" : "Muerte de %s", "Calendar:" : "Calendario:", diff --git a/apps/dav/l10n/es.json b/apps/dav/l10n/es.json index 4f7161fe3c5..ef5128cc455 100644 --- a/apps/dav/l10n/es.json +++ b/apps/dav/l10n/es.json @@ -1,5 +1,6 @@ { "translations": { "Calendar" : "Calendario", + "To-dos" : "Tareas", "Personal" : "Personal", "{actor} created calendar {calendar}" : "{actor} creó el calendario {calendar}", "You created calendar {calendar}" : "Usted creó el calendario {calendar}", @@ -34,9 +35,22 @@ "{actor} restored event {event} of calendar {calendar}" : "{actor} ha restablecido el evento {event} del calendario {calendar}", "You restored event {event} of calendar {calendar}" : "Has reestablecido el evento {event} del calendario {calendar}", "Busy" : "Ocupado", + "{actor} created to-do {todo} in list {calendar}" : "{actor} ha creado la tarea {todo} en la lista {calendar}", + "You created to-do {todo} in list {calendar}" : "Has creado la tarea {todo} en la lista {calendar}", + "{actor} deleted to-do {todo} from list {calendar}" : "{actor} ha eliminado la tarea{todo} de la lista {calendar}", + "You deleted to-do {todo} from list {calendar}" : "Has eliminado la tarea {todo} de la lista {calendar}", + "{actor} updated to-do {todo} in list {calendar}" : "{actor} ha actualizado la tarea {todo} en la lista {calendar}", + "You updated to-do {todo} in list {calendar}" : "Has actualizado la tarea {todo} en la lista {calendar}", + "{actor} solved to-do {todo} in list {calendar}" : "{actor} ha resuelto la tarea {todo} de la lista {calendar}", + "You solved to-do {todo} in list {calendar}" : "Has resuelto la tarea {todo} de la lista {calendar}", + "{actor} reopened to-do {todo} in list {calendar}" : "{actor} ha reabierto la tarea {todo} en la lista {calendar}", + "You reopened to-do {todo} in list {calendar}" : "Has reabierto la tarea {todo} en la lista {calendar}", + "{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{actor} ha movido la tarea {todo} de la lista {sourceCalendar} a la lista{targetCalendar}", + "You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "Has movido la tarea {todo} de la lista {sourceCalendar} a la lista {targetCalendar}", "Calendar, contacts and tasks" : "Calendario, contactos y tareas", "A <strong>calendar</strong> was modified" : "Un <strong>calendario</strong> fue modificado.", "A calendar <strong>event</strong> was modified" : "Un <strong>evento</strong> del calendario fue modificado.", + "A calendar <strong>to-do</strong> was modified" : "Se ha modificado una <strong>tarea</strong> de calendario", "Contact birthdays" : "Cumpleaños del contacto", "Death of %s" : "Muerte de %s", "Calendar:" : "Calendario:", diff --git a/apps/dav/l10n/eu.js b/apps/dav/l10n/eu.js index 8f0868cc37f..061dd72447d 100644 --- a/apps/dav/l10n/eu.js +++ b/apps/dav/l10n/eu.js @@ -37,6 +37,18 @@ OC.L10N.register( "{actor} restored event {event} of calendar {calendar}" : "{actor}-(e)k berrezarri du {calendar} egutegiko {event} gertaera ", "You restored event {event} of calendar {calendar}" : "Berrezarri duzu {calendar} egutegiko {event} gertaera ", "Busy" : "Lanpetua", + "{actor} created to-do {todo} in list {calendar}" : "{actor}-(e)k {calendar} zerrendan {todo} egitekoa sortu du", + "You created to-do {todo} in list {calendar}" : "{calendar} zerrendan {todo} egitekoa sortu duzu", + "{actor} deleted to-do {todo} from list {calendar}" : "{actor}-(e)k {calendar} zerrendan {todo} egitekoa ezabatu du", + "You deleted to-do {todo} from list {calendar}" : " {calendar} zerrendan {todo} egitekoa ezabatu duzu", + "{actor} updated to-do {todo} in list {calendar}" : "{actor}-(e)k {calendar} zerrendan {todo} egitekoa eguneratu du", + "You updated to-do {todo} in list {calendar}" : "{calendar} zerrendan {todo} egitekoa eguneratu duzu", + "{actor} solved to-do {todo} in list {calendar}" : "{actor}-(e)k {calendar} zerrendan {todo} egitekoa bukatu du.", + "You solved to-do {todo} in list {calendar}" : " {calendar} zerrendan {todo} egitekoa bukatu duzu", + "{actor} reopened to-do {todo} in list {calendar}" : "{actor}-(e)k {calendar} zerrendan {todo} egitekoa berrireki du", + "You reopened to-do {todo} in list {calendar}" : "{calendar} zerrendan {todo} egitekoa berrireki duzu", + "{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{actor}-(e)k {todo} egitekoa {sourceCalendar} zerrendatik {targetCalendar} zerrendara mugitu du", + "You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{todo} egitekoa {sourceCalendar} zerrendatik {targetCalendar} zerrendara mugitu duzu", "Calendar, contacts and tasks" : "Egutegia, kontaktuak eta atazak", "A <strong>calendar</strong> was modified" : "Egutegia aldatu da", "A calendar <strong>event</strong> was modified" : "Egutegiaren <strong>gertaera</strong> bat aldatu da", diff --git a/apps/dav/l10n/eu.json b/apps/dav/l10n/eu.json index 90faed77b8b..89a218e98de 100644 --- a/apps/dav/l10n/eu.json +++ b/apps/dav/l10n/eu.json @@ -35,6 +35,18 @@ "{actor} restored event {event} of calendar {calendar}" : "{actor}-(e)k berrezarri du {calendar} egutegiko {event} gertaera ", "You restored event {event} of calendar {calendar}" : "Berrezarri duzu {calendar} egutegiko {event} gertaera ", "Busy" : "Lanpetua", + "{actor} created to-do {todo} in list {calendar}" : "{actor}-(e)k {calendar} zerrendan {todo} egitekoa sortu du", + "You created to-do {todo} in list {calendar}" : "{calendar} zerrendan {todo} egitekoa sortu duzu", + "{actor} deleted to-do {todo} from list {calendar}" : "{actor}-(e)k {calendar} zerrendan {todo} egitekoa ezabatu du", + "You deleted to-do {todo} from list {calendar}" : " {calendar} zerrendan {todo} egitekoa ezabatu duzu", + "{actor} updated to-do {todo} in list {calendar}" : "{actor}-(e)k {calendar} zerrendan {todo} egitekoa eguneratu du", + "You updated to-do {todo} in list {calendar}" : "{calendar} zerrendan {todo} egitekoa eguneratu duzu", + "{actor} solved to-do {todo} in list {calendar}" : "{actor}-(e)k {calendar} zerrendan {todo} egitekoa bukatu du.", + "You solved to-do {todo} in list {calendar}" : " {calendar} zerrendan {todo} egitekoa bukatu duzu", + "{actor} reopened to-do {todo} in list {calendar}" : "{actor}-(e)k {calendar} zerrendan {todo} egitekoa berrireki du", + "You reopened to-do {todo} in list {calendar}" : "{calendar} zerrendan {todo} egitekoa berrireki duzu", + "{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{actor}-(e)k {todo} egitekoa {sourceCalendar} zerrendatik {targetCalendar} zerrendara mugitu du", + "You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{todo} egitekoa {sourceCalendar} zerrendatik {targetCalendar} zerrendara mugitu duzu", "Calendar, contacts and tasks" : "Egutegia, kontaktuak eta atazak", "A <strong>calendar</strong> was modified" : "Egutegia aldatu da", "A calendar <strong>event</strong> was modified" : "Egutegiaren <strong>gertaera</strong> bat aldatu da", diff --git a/apps/dav/l10n/pt_BR.js b/apps/dav/l10n/pt_BR.js index 969cd3e66ab..a28a23a147e 100644 --- a/apps/dav/l10n/pt_BR.js +++ b/apps/dav/l10n/pt_BR.js @@ -2,6 +2,7 @@ OC.L10N.register( "dav", { "Calendar" : "Calendário", + "To-dos" : "Tarefas", "Personal" : "Pessoal", "{actor} created calendar {calendar}" : "{actor} criou o calendário {calendar}", "You created calendar {calendar}" : "Você criou o calendário {calendar}", @@ -36,9 +37,22 @@ OC.L10N.register( "{actor} restored event {event} of calendar {calendar}" : "{actor} restaurou o evento {event} do calendário {calendar}", "You restored event {event} of calendar {calendar}" : "Você restaurou o evento {event} do calendário {calendar}", "Busy" : "Ocupado", + "{actor} created to-do {todo} in list {calendar}" : "{actor} criou tarefa {todo} na lista {calendar}", + "You created to-do {todo} in list {calendar}" : "Você criou a tarefa {todo} na lista {calendar}", + "{actor} deleted to-do {todo} from list {calendar}" : "{actor} excluiu a tarefa {todo} da lista {calendar}", + "You deleted to-do {todo} from list {calendar}" : "Você excluiu a tarefa {todo} da lista {calendar}", + "{actor} updated to-do {todo} in list {calendar}" : "{actor} atualizou a tarefa {todo} na lista {calendar}", + "You updated to-do {todo} in list {calendar}" : "Você atualizou a tarefa {todo} na lista {calendar}", + "{actor} solved to-do {todo} in list {calendar}" : "{actor} resolveu a tarefa {todo} na lista {calendar}", + "You solved to-do {todo} in list {calendar}" : "Você resolveu a tarefa {todo} na lista {calendar}", + "{actor} reopened to-do {todo} in list {calendar}" : "{actor} reabriu a tarefa {todo} na lista {calendar}", + "You reopened to-do {todo} in list {calendar}" : "Você reabriu a tarefa {todo} na lista {calendar}", + "{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{actor} moveu a tarefa {todo} da lista {sourceCalendar} para a lista {targetCalendar}", + "You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "Você moveu a tarefa {todo} da lista {sourceCalendar} para a lista {targetCalendar}", "Calendar, contacts and tasks" : "Calendário, contatos e tarefas", "A <strong>calendar</strong> was modified" : "Um <strong>calendário</strong> foi modificado", "A calendar <strong>event</strong> was modified" : "Um <strong>evento</strong> do calendário foi modificado", + "A calendar <strong>to-do</strong> was modified" : "Um calendário <strong>tarefa</strong> foi modificado", "Contact birthdays" : "Aniversário dos contatos", "Death of %s" : "Morte de %s", "Calendar:" : "Calendário:", diff --git a/apps/dav/l10n/pt_BR.json b/apps/dav/l10n/pt_BR.json index ff3ace30737..71161f3f56d 100644 --- a/apps/dav/l10n/pt_BR.json +++ b/apps/dav/l10n/pt_BR.json @@ -1,5 +1,6 @@ { "translations": { "Calendar" : "Calendário", + "To-dos" : "Tarefas", "Personal" : "Pessoal", "{actor} created calendar {calendar}" : "{actor} criou o calendário {calendar}", "You created calendar {calendar}" : "Você criou o calendário {calendar}", @@ -34,9 +35,22 @@ "{actor} restored event {event} of calendar {calendar}" : "{actor} restaurou o evento {event} do calendário {calendar}", "You restored event {event} of calendar {calendar}" : "Você restaurou o evento {event} do calendário {calendar}", "Busy" : "Ocupado", + "{actor} created to-do {todo} in list {calendar}" : "{actor} criou tarefa {todo} na lista {calendar}", + "You created to-do {todo} in list {calendar}" : "Você criou a tarefa {todo} na lista {calendar}", + "{actor} deleted to-do {todo} from list {calendar}" : "{actor} excluiu a tarefa {todo} da lista {calendar}", + "You deleted to-do {todo} from list {calendar}" : "Você excluiu a tarefa {todo} da lista {calendar}", + "{actor} updated to-do {todo} in list {calendar}" : "{actor} atualizou a tarefa {todo} na lista {calendar}", + "You updated to-do {todo} in list {calendar}" : "Você atualizou a tarefa {todo} na lista {calendar}", + "{actor} solved to-do {todo} in list {calendar}" : "{actor} resolveu a tarefa {todo} na lista {calendar}", + "You solved to-do {todo} in list {calendar}" : "Você resolveu a tarefa {todo} na lista {calendar}", + "{actor} reopened to-do {todo} in list {calendar}" : "{actor} reabriu a tarefa {todo} na lista {calendar}", + "You reopened to-do {todo} in list {calendar}" : "Você reabriu a tarefa {todo} na lista {calendar}", + "{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{actor} moveu a tarefa {todo} da lista {sourceCalendar} para a lista {targetCalendar}", + "You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "Você moveu a tarefa {todo} da lista {sourceCalendar} para a lista {targetCalendar}", "Calendar, contacts and tasks" : "Calendário, contatos e tarefas", "A <strong>calendar</strong> was modified" : "Um <strong>calendário</strong> foi modificado", "A calendar <strong>event</strong> was modified" : "Um <strong>evento</strong> do calendário foi modificado", + "A calendar <strong>to-do</strong> was modified" : "Um calendário <strong>tarefa</strong> foi modificado", "Contact birthdays" : "Aniversário dos contatos", "Death of %s" : "Morte de %s", "Calendar:" : "Calendário:", diff --git a/apps/dav/l10n/zh_HK.js b/apps/dav/l10n/zh_HK.js index 40d2ac629c4..6f52575901d 100644 --- a/apps/dav/l10n/zh_HK.js +++ b/apps/dav/l10n/zh_HK.js @@ -2,6 +2,7 @@ OC.L10N.register( "dav", { "Calendar" : "日曆", + "To-dos" : "待辦事項", "Personal" : "個人", "{actor} created calendar {calendar}" : "{actor} 建立了日曆 {calendar}", "You created calendar {calendar}" : "您建立了日曆 {calendar}", @@ -36,9 +37,22 @@ OC.L10N.register( "{actor} restored event {event} of calendar {calendar}" : "{actor} 復原了日曆 {calendar} 的活動 {event}", "You restored event {event} of calendar {calendar}" : "您復原了日曆 {calendar} 的活動 {event}", "Busy" : "忙碌中", + "{actor} created to-do {todo} in list {calendar}" : "{actor} 在任務清單 {calendar} 中建立了待辦事項 {todo}", + "You created to-do {todo} in list {calendar}" : "您在任務清單 {calendar} 中建立了待辦事項 {todo}", + "{actor} deleted to-do {todo} from list {calendar}" : "{actor} 從任務清單 {calendar} 中刪除了待辦事項 {todo}", + "You deleted to-do {todo} from list {calendar}" : "您從任務清單 {calendar} 中刪除了待辦事項 {todo}", + "{actor} updated to-do {todo} in list {calendar}" : "{actor} 從任務清單 {calendar} 中更新了待辦事項 {todo}", + "You updated to-do {todo} in list {calendar}" : "您在任務清單 {calendar} 中更新了待辦事項 {todo}", + "{actor} solved to-do {todo} in list {calendar}" : "{actor} 解决了任務清單 {calendar} 中的代辦事項 {todo}", + "You solved to-do {todo} in list {calendar}" : "您解决了任務清單 {calendar} 中的代辦事項 {todo}", + "{actor} reopened to-do {todo} in list {calendar}" : "{actor} 重新開啟了任務清單 {calendar} 中的代辦事項 {todo}", + "You reopened to-do {todo} in list {calendar}" : "你重新開啟了任務清單 {calendar} 中的代辦事項 {todo}", + "{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{actor} 將待辦事項 {todo} 從清單 {sourceCalendar} 移到清單 {targetCalendar}", + "You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "您將待辦事項 {todo} 從清單 {sourceCalendar} 移到清單 {targetCalendar}", "Calendar, contacts and tasks" : "日曆、聯絡人和任務", "A <strong>calendar</strong> was modified" : "<strong>日曆</strong>被修改", "A calendar <strong>event</strong> was modified" : "日曆<strong>活動</strong>被修改", + "A calendar <strong>to-do</strong> was modified" : "日曆<strong>代辦事項</strong>被修改", "Contact birthdays" : "聯絡人生日", "Death of %s" : "%s之卒", "Calendar:" : "日曆:", diff --git a/apps/dav/l10n/zh_HK.json b/apps/dav/l10n/zh_HK.json index 6f74dfb0e13..0e6e6062f55 100644 --- a/apps/dav/l10n/zh_HK.json +++ b/apps/dav/l10n/zh_HK.json @@ -1,5 +1,6 @@ { "translations": { "Calendar" : "日曆", + "To-dos" : "待辦事項", "Personal" : "個人", "{actor} created calendar {calendar}" : "{actor} 建立了日曆 {calendar}", "You created calendar {calendar}" : "您建立了日曆 {calendar}", @@ -34,9 +35,22 @@ "{actor} restored event {event} of calendar {calendar}" : "{actor} 復原了日曆 {calendar} 的活動 {event}", "You restored event {event} of calendar {calendar}" : "您復原了日曆 {calendar} 的活動 {event}", "Busy" : "忙碌中", + "{actor} created to-do {todo} in list {calendar}" : "{actor} 在任務清單 {calendar} 中建立了待辦事項 {todo}", + "You created to-do {todo} in list {calendar}" : "您在任務清單 {calendar} 中建立了待辦事項 {todo}", + "{actor} deleted to-do {todo} from list {calendar}" : "{actor} 從任務清單 {calendar} 中刪除了待辦事項 {todo}", + "You deleted to-do {todo} from list {calendar}" : "您從任務清單 {calendar} 中刪除了待辦事項 {todo}", + "{actor} updated to-do {todo} in list {calendar}" : "{actor} 從任務清單 {calendar} 中更新了待辦事項 {todo}", + "You updated to-do {todo} in list {calendar}" : "您在任務清單 {calendar} 中更新了待辦事項 {todo}", + "{actor} solved to-do {todo} in list {calendar}" : "{actor} 解决了任務清單 {calendar} 中的代辦事項 {todo}", + "You solved to-do {todo} in list {calendar}" : "您解决了任務清單 {calendar} 中的代辦事項 {todo}", + "{actor} reopened to-do {todo} in list {calendar}" : "{actor} 重新開啟了任務清單 {calendar} 中的代辦事項 {todo}", + "You reopened to-do {todo} in list {calendar}" : "你重新開啟了任務清單 {calendar} 中的代辦事項 {todo}", + "{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{actor} 將待辦事項 {todo} 從清單 {sourceCalendar} 移到清單 {targetCalendar}", + "You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "您將待辦事項 {todo} 從清單 {sourceCalendar} 移到清單 {targetCalendar}", "Calendar, contacts and tasks" : "日曆、聯絡人和任務", "A <strong>calendar</strong> was modified" : "<strong>日曆</strong>被修改", "A calendar <strong>event</strong> was modified" : "日曆<strong>活動</strong>被修改", + "A calendar <strong>to-do</strong> was modified" : "日曆<strong>代辦事項</strong>被修改", "Contact birthdays" : "聯絡人生日", "Death of %s" : "%s之卒", "Calendar:" : "日曆:", diff --git a/apps/dav/lib/Connector/Sabre/Directory.php b/apps/dav/lib/Connector/Sabre/Directory.php index 5280511d5be..b575a051b2a 100644 --- a/apps/dav/lib/Connector/Sabre/Directory.php +++ b/apps/dav/lib/Connector/Sabre/Directory.php @@ -66,7 +66,7 @@ class Directory extends \OCA\DAV\Connector\Sabre\Node implements \Sabre\DAV\ICol /** Cached quota info */ private ?array $quotaInfo = null; - private ?ObjectTree $tree = null; + private ?CachingTree $tree = null; /** @var array<string, array<int, FileMetadata>> */ private array $metadata = []; @@ -74,7 +74,7 @@ class Directory extends \OCA\DAV\Connector\Sabre\Node implements \Sabre\DAV\ICol /** * Sets up the node, expects a full path name */ - public function __construct(View $view, FileInfo $info, ?ObjectTree $tree = null, IShareManager $shareManager = null) { + public function __construct(View $view, FileInfo $info, ?CachingTree $tree = null, IShareManager $shareManager = null) { parent::__construct($view, $info, $shareManager); $this->tree = $tree; } diff --git a/apps/dav/tests/unit/Comments/CommentsNodeTest.php b/apps/dav/tests/unit/Comments/CommentsNodeTest.php index ea3a1952e45..54d410b609a 100644 --- a/apps/dav/tests/unit/Comments/CommentsNodeTest.php +++ b/apps/dav/tests/unit/Comments/CommentsNodeTest.php @@ -405,6 +405,7 @@ class CommentsNodeTest extends \Test\TestCase { $ns . 'referenceId' => 'ref', $ns . 'isUnread' => null, $ns . 'reactions' => [], + $ns . 'expireDate' => new \DateTime('2016-01-12 19:00:00'), ]; $this->commentsManager->expects($this->exactly(2)) @@ -474,6 +475,10 @@ class CommentsNodeTest extends \Test\TestCase { ->method('getReferenceId') ->willReturn($expected[$ns . 'referenceId']); + $this->comment->expects($this->once()) + ->method('getExpireDate') + ->willReturn($expected[$ns . 'expireDate']); + $user = $this->getMockBuilder(IUser::class) ->disableOriginalConstructor() ->getMock(); diff --git a/apps/files/appinfo/routes.php b/apps/files/appinfo/routes.php index a98170363e9..2aeef05558c 100644 --- a/apps/files/appinfo/routes.php +++ b/apps/files/appinfo/routes.php @@ -99,7 +99,7 @@ $application->registerRoutes( ], [ 'name' => 'ajax#getStorageStats', - 'url' => '/ajax/getstoragestats.php', + 'url' => '/ajax/getstoragestats', 'verb' => 'GET', ], [ diff --git a/apps/files/js/files.js b/apps/files/js/files.js index ae247584682..6aa871d99c0 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -25,7 +25,7 @@ state.call.abort(); } state.dir = currentDir; - state.call = $.getJSON(OC.filePath('files','ajax','getstoragestats.php') + '?dir=' + encodeURIComponent(currentDir),function(response) { + state.call = $.getJSON(OC.filePath('files','ajax','getstoragestats') + '?dir=' + encodeURIComponent(currentDir),function(response) { state.dir = null; state.call = null; Files.updateMaxUploadFilesize(response); @@ -37,7 +37,7 @@ }, _updateStorageQuotas: function() { var state = Files.updateStorageQuotas; - state.call = $.getJSON(OC.filePath('files','ajax','getstoragestats.php'),function(response) { + state.call = $.getJSON(OC.filePath('files','ajax','getstoragestats'),function(response) { Files.updateQuota(response); }); }, diff --git a/apps/files/l10n/da.js b/apps/files/l10n/da.js index 0aa2d0a4eab..d07695e4d3c 100644 --- a/apps/files/l10n/da.js +++ b/apps/files/l10n/da.js @@ -21,6 +21,7 @@ OC.L10N.register( "Target folder \"{dir}\" does not exist any more" : "Destinationsmappen \"{dir}\" findes ikke længere", "Not enough free space" : "Ikke nok fri plads", "An unknown error has occurred" : "En ukendt fejl er opstået", + "File could not be uploaded" : "Filen kunne ikke uploades", "Uploading …" : "Uploader ...", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} af {totalSize} ({bitrate})", "Uploading that item is not supported" : "Upload af dette er ikke supporteret", @@ -41,6 +42,8 @@ OC.L10N.register( "Details" : "Detaljer", "Please select tag(s) to add to the selection" : "Vælg venligst tag(s) for at tilføje til udvalget", "Apply tag(s) to selection" : "Anvend tag(s) på markeringen", + "Select directory \"{dirName}\"" : "Vælg katalog \"{dirName}\"", + "Select file \"{fileName}\"" : "Vælg fil \"{fileName}\"", "Pending" : "Afventer", "Unable to determine date" : "Kan ikke fastslå datoen", "This operation is forbidden" : "Denne operation er forbudt", @@ -70,6 +73,7 @@ OC.L10N.register( "_%n file_::_%n files_" : ["%n fil","%n filer"], "{dirs} and {files}" : "{dirs} og {files}", "_including %n hidden_::_including %n hidden_" : ["%n skujlte inkluderet","%n skujlte inkluderet"], + "You do not have permission to upload or create files here" : "Du har ikke tilladelse til at uploade eller oprette filer her", "_Uploading %n file_::_Uploading %n files_" : ["Uploader %n fil","Uploader %n filer"], "New" : "Ny", "Select file range" : "Vælg filområde", @@ -168,6 +172,7 @@ OC.L10N.register( "Transfer {path} to {userid}" : "Overfør {path} til {userid}", "Invalid path selected" : "Ugyldig filsti valgt", "Ownership transfer request sent" : "Anmodning om ejerskabsoverdragelse sendt", + "Cannot transfer ownership of a file or folder you do not own" : "Kan ikke overføre ejerskab af en fil eller mappe, du ikke ejer", "Tags" : "Mærker", "Unable to change the favourite state of the file" : "Kan ikke ændre favorittilstanden for filen", "Error while loading the file data" : "Fejl under indlæsning af fildata", diff --git a/apps/files/l10n/da.json b/apps/files/l10n/da.json index 8671562f2f9..d3177ba5057 100644 --- a/apps/files/l10n/da.json +++ b/apps/files/l10n/da.json @@ -19,6 +19,7 @@ "Target folder \"{dir}\" does not exist any more" : "Destinationsmappen \"{dir}\" findes ikke længere", "Not enough free space" : "Ikke nok fri plads", "An unknown error has occurred" : "En ukendt fejl er opstået", + "File could not be uploaded" : "Filen kunne ikke uploades", "Uploading …" : "Uploader ...", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} af {totalSize} ({bitrate})", "Uploading that item is not supported" : "Upload af dette er ikke supporteret", @@ -39,6 +40,8 @@ "Details" : "Detaljer", "Please select tag(s) to add to the selection" : "Vælg venligst tag(s) for at tilføje til udvalget", "Apply tag(s) to selection" : "Anvend tag(s) på markeringen", + "Select directory \"{dirName}\"" : "Vælg katalog \"{dirName}\"", + "Select file \"{fileName}\"" : "Vælg fil \"{fileName}\"", "Pending" : "Afventer", "Unable to determine date" : "Kan ikke fastslå datoen", "This operation is forbidden" : "Denne operation er forbudt", @@ -68,6 +71,7 @@ "_%n file_::_%n files_" : ["%n fil","%n filer"], "{dirs} and {files}" : "{dirs} og {files}", "_including %n hidden_::_including %n hidden_" : ["%n skujlte inkluderet","%n skujlte inkluderet"], + "You do not have permission to upload or create files here" : "Du har ikke tilladelse til at uploade eller oprette filer her", "_Uploading %n file_::_Uploading %n files_" : ["Uploader %n fil","Uploader %n filer"], "New" : "Ny", "Select file range" : "Vælg filområde", @@ -166,6 +170,7 @@ "Transfer {path} to {userid}" : "Overfør {path} til {userid}", "Invalid path selected" : "Ugyldig filsti valgt", "Ownership transfer request sent" : "Anmodning om ejerskabsoverdragelse sendt", + "Cannot transfer ownership of a file or folder you do not own" : "Kan ikke overføre ejerskab af en fil eller mappe, du ikke ejer", "Tags" : "Mærker", "Unable to change the favourite state of the file" : "Kan ikke ændre favorittilstanden for filen", "Error while loading the file data" : "Fejl under indlæsning af fildata", diff --git a/apps/files_sharing/l10n/da.js b/apps/files_sharing/l10n/da.js index a0e0254a6ca..1fca2d39bce 100644 --- a/apps/files_sharing/l10n/da.js +++ b/apps/files_sharing/l10n/da.js @@ -130,6 +130,7 @@ OC.L10N.register( "Allow resharing" : "Tillad videredeling", "Expiration date enforced" : "Udløbsdato tvungen", "Set expiration date" : "Angiv udløbsdato", + "Enter a date" : "Indtast en dato", "Note to recipient" : "Note til modtager", "Unshare" : "Fjern deling", "group" : "gruppe", diff --git a/apps/files_sharing/l10n/da.json b/apps/files_sharing/l10n/da.json index 8855fcbebe4..350985a6de9 100644 --- a/apps/files_sharing/l10n/da.json +++ b/apps/files_sharing/l10n/da.json @@ -128,6 +128,7 @@ "Allow resharing" : "Tillad videredeling", "Expiration date enforced" : "Udløbsdato tvungen", "Set expiration date" : "Angiv udløbsdato", + "Enter a date" : "Indtast en dato", "Note to recipient" : "Note til modtager", "Unshare" : "Fjern deling", "group" : "gruppe", diff --git a/apps/settings/l10n/el.js b/apps/settings/l10n/el.js index 1ee32a4ca86..7ad8f009232 100644 --- a/apps/settings/l10n/el.js +++ b/apps/settings/l10n/el.js @@ -125,7 +125,7 @@ OC.L10N.register( "There are some user imported SSL certificates present, that are not used anymore with Nextcloud 21. They can be imported on the command line via \"occ security:certificates:import\" command. Their paths inside the data directory are shown below." : "Υπάρχουν ορισμένα πιστοποιητικά SSL εισαγόμενα από τον χρήστη, τα οποία δεν χρησιμοποιούνται πλέον με το Nextcloud 21. Μπορούν να εισαχθούν στη γραμμή εντολών μέσω της εντολής \"occ security: documents: import\". Οι διαδρομές τους στον κατάλογο δεδομένων εμφανίζονται παρακάτω.", "The old server-side-encryption format is enabled. We recommend disabling this." : "Η παλιά διαμόρφωση κρυπτογράφησης από διακομιστή σε διακομιστή είναι ενεργοποιημένη. Σας προτείνουμε να την απενεργοποιήσετε.", "Nextcloud settings" : "Ρυθμίσεις Nextcloud ", - "Administration privileges" : "Προνόμια διαχείρισης", + "Administration privileges" : "Προνόμια διαχειριστή", "Here you can decide which group can access certain sections of the administration settings." : "Εδώ ορίστε ποια ομάδα μπορεί να έχει πρόσβαση σε ορισμένες ενότητες των ρυθμίσεων διαχείρισης.", "None" : "Τίποτα", "Unable to modify setting" : "Δεν είναι δυνατή η τροποποίηση της ρύθμισης", diff --git a/apps/settings/l10n/el.json b/apps/settings/l10n/el.json index 0ed7985b99e..1d4a465e8b3 100644 --- a/apps/settings/l10n/el.json +++ b/apps/settings/l10n/el.json @@ -123,7 +123,7 @@ "There are some user imported SSL certificates present, that are not used anymore with Nextcloud 21. They can be imported on the command line via \"occ security:certificates:import\" command. Their paths inside the data directory are shown below." : "Υπάρχουν ορισμένα πιστοποιητικά SSL εισαγόμενα από τον χρήστη, τα οποία δεν χρησιμοποιούνται πλέον με το Nextcloud 21. Μπορούν να εισαχθούν στη γραμμή εντολών μέσω της εντολής \"occ security: documents: import\". Οι διαδρομές τους στον κατάλογο δεδομένων εμφανίζονται παρακάτω.", "The old server-side-encryption format is enabled. We recommend disabling this." : "Η παλιά διαμόρφωση κρυπτογράφησης από διακομιστή σε διακομιστή είναι ενεργοποιημένη. Σας προτείνουμε να την απενεργοποιήσετε.", "Nextcloud settings" : "Ρυθμίσεις Nextcloud ", - "Administration privileges" : "Προνόμια διαχείρισης", + "Administration privileges" : "Προνόμια διαχειριστή", "Here you can decide which group can access certain sections of the administration settings." : "Εδώ ορίστε ποια ομάδα μπορεί να έχει πρόσβαση σε ορισμένες ενότητες των ρυθμίσεων διαχείρισης.", "None" : "Τίποτα", "Unable to modify setting" : "Δεν είναι δυνατή η τροποποίηση της ρύθμισης", diff --git a/apps/settings/l10n/fa.js b/apps/settings/l10n/fa.js index 9d9ecf28b5b..36596c3c805 100644 --- a/apps/settings/l10n/fa.js +++ b/apps/settings/l10n/fa.js @@ -2,8 +2,13 @@ OC.L10N.register( "settings", { "Private" : "خصوصی", + "Only visible to people matched via phone number integration through Talk on mobile" : "فقط برای کاربرانی فعال است که از طریق یکپارچه سازی شماره تلفن در برنامه Talk روی تلفن همراه مطابقت داشته باشند", "Local" : "محلی", + "Only visible to people on this instance and guests" : "فقط برای کاربران این نسخه و مهمانان قابل مشاهده است", "Federated" : "فدرال", + "Only synchronize to trusted servers" : "فقط با سرورهای قابل اعتماد همگام سازی شود", + "Published" : "منتشر شده", + "Synchronize to trusted servers and the global and public address book" : "با سرورهای قابل اعتماد و دفترچه آدرس عمومی و همگانی همگام سازی شود", "Verify" : "تأیید کنید", "Verifying …" : "در حال تایید ...", "Unable to change password" : "نمیتوان رمز را تغییر داد", @@ -33,10 +38,12 @@ OC.L10N.register( "{actor} changed your password" : "{actor} رمز عبور خود را تغییر داد", "You changed your password" : "رمزعبور خود را تغییر دادید", "Your password was reset by an administrator" : "رمز عبور شما توسط یه مدیر تنظیم مجدد شد.", + "Your password was reset" : "رمز عبور شما رمز عبور شما بازنشانی شد", "{actor} changed your email address" : "{actor} پست الکترونیک خود را تغییر داد", "You changed your email address" : "پست الکترونیک خود را تغییر دادید", "Your email address was changed by an administrator" : "پست الکترونیک شما توسط یه مدیر تنظیم مجدد شد.", "You created app password \"{token}\"" : "رمز عبور برنامه توسط شما ایجاد شده است \"{token}\"", + "An administrator created app password \"{token}\"" : "یک مدیر \"{token}\" گذرواژه برنامه را ایجاد کرد", "You deleted app password \"{token}\"" : "رمز عبور برنامه توسط شما حذف شده است \"{token}\"", "You renamed app password \"{token}\" to \"{newToken}\"" : "شما رمزعبور برنامه را از \"{token}\" به {newToken} تغییر دادید ", "You granted filesystem access to app password \"{token}\"" : "دسترسی شما به فایل سیستم به رمز ورود برنامه اعطا گردید \"{token}\"", @@ -47,14 +54,19 @@ OC.L10N.register( "Remote wipe was started on %1$s" : "پاک کردن از راه دور شروع شد %1$s", "Remote wipe has finished on %1$s" : "پاک کردن از راه دور به پایان رسید%1$s", "Your <strong>password</strong> or <strong>email</strong> was modified" : "<strong>پسورد</strong> و یا <strong>پست الکترونیک</strong> شما ویراش شد.", + "Could not remove app." : "امکان حذف برنامه وجود ندارد.", + "Could not update app." : "برنامه را نمی توان به روزرسانی کرد .", "Wrong password" : "رمز عبور اشتباه است", + "Unable to change personal password" : "نمیتوان گذرواژه شخصی را تغییر داد", "Saved" : "ذخیره شد", "No user supplied" : "هیچ کاربری تعریف نشده است", "Authentication error" : "خطا در اعتبار سنجی", "Please provide an admin recovery password; otherwise, all user data will be lost." : "لطفاً رمز عبور بازیابی مدیر را ارائه دهید. در غیر این صورت ، تمام داده های کاربر از بین می رود.", "Wrong admin recovery password. Please check the password and try again." : "رمز مدیریتی بازیابی غلط است. لطفاً رمز را کنترل کرده و دوباره امتحان نمایید.", + "Backend does not support password change, but the user's encryption key was updated." : " سرور از تغییرات گذرواژه پشتیبانی نمی کند، اما کلید رمزگذاری کاربر به روز شد.", "Federated Cloud Sharing" : "اشتراک گذاری ابر فدرال", "cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably." : "cURL از نسخه (%2$s) منسوخ شده %1$s استفاده می کند. لطفا سیستم عامل خود را به روز کنید یا ویژگی هایی از قبیل %3$s قابل اعتماد کار نمی کنند.", + "Could not determine if TLS version of cURL is outdated or not because an error happened during the HTTPS request against https://nextcloud.com. Please check the Nextcloud log file for more details." : "نمی توان تعیین کرد که آیا نسخه TLS متعلق به cURL قدیمی است یا خیر. زیرا خطایی در طول درخواست HTTPS به https://nextcloud.com رخ داده است. لطفاً فایل گزارش نکستکلود را برای جزئیات بیشتر بررسی کنید.", "Invalid SMTP password." : "گذرواژه SMTP نامعتبر است.", "Email setting test" : "تست تنظیم پست الکترونیک", "Well done, %s!" : "انجام شد،%s!", diff --git a/apps/settings/l10n/fa.json b/apps/settings/l10n/fa.json index 119afbbf433..47daa51b79d 100644 --- a/apps/settings/l10n/fa.json +++ b/apps/settings/l10n/fa.json @@ -1,7 +1,12 @@ { "translations": { "Private" : "خصوصی", + "Only visible to people matched via phone number integration through Talk on mobile" : "فقط برای کاربرانی فعال است که از طریق یکپارچه سازی شماره تلفن در برنامه Talk روی تلفن همراه مطابقت داشته باشند", "Local" : "محلی", + "Only visible to people on this instance and guests" : "فقط برای کاربران این نسخه و مهمانان قابل مشاهده است", "Federated" : "فدرال", + "Only synchronize to trusted servers" : "فقط با سرورهای قابل اعتماد همگام سازی شود", + "Published" : "منتشر شده", + "Synchronize to trusted servers and the global and public address book" : "با سرورهای قابل اعتماد و دفترچه آدرس عمومی و همگانی همگام سازی شود", "Verify" : "تأیید کنید", "Verifying …" : "در حال تایید ...", "Unable to change password" : "نمیتوان رمز را تغییر داد", @@ -31,10 +36,12 @@ "{actor} changed your password" : "{actor} رمز عبور خود را تغییر داد", "You changed your password" : "رمزعبور خود را تغییر دادید", "Your password was reset by an administrator" : "رمز عبور شما توسط یه مدیر تنظیم مجدد شد.", + "Your password was reset" : "رمز عبور شما رمز عبور شما بازنشانی شد", "{actor} changed your email address" : "{actor} پست الکترونیک خود را تغییر داد", "You changed your email address" : "پست الکترونیک خود را تغییر دادید", "Your email address was changed by an administrator" : "پست الکترونیک شما توسط یه مدیر تنظیم مجدد شد.", "You created app password \"{token}\"" : "رمز عبور برنامه توسط شما ایجاد شده است \"{token}\"", + "An administrator created app password \"{token}\"" : "یک مدیر \"{token}\" گذرواژه برنامه را ایجاد کرد", "You deleted app password \"{token}\"" : "رمز عبور برنامه توسط شما حذف شده است \"{token}\"", "You renamed app password \"{token}\" to \"{newToken}\"" : "شما رمزعبور برنامه را از \"{token}\" به {newToken} تغییر دادید ", "You granted filesystem access to app password \"{token}\"" : "دسترسی شما به فایل سیستم به رمز ورود برنامه اعطا گردید \"{token}\"", @@ -45,14 +52,19 @@ "Remote wipe was started on %1$s" : "پاک کردن از راه دور شروع شد %1$s", "Remote wipe has finished on %1$s" : "پاک کردن از راه دور به پایان رسید%1$s", "Your <strong>password</strong> or <strong>email</strong> was modified" : "<strong>پسورد</strong> و یا <strong>پست الکترونیک</strong> شما ویراش شد.", + "Could not remove app." : "امکان حذف برنامه وجود ندارد.", + "Could not update app." : "برنامه را نمی توان به روزرسانی کرد .", "Wrong password" : "رمز عبور اشتباه است", + "Unable to change personal password" : "نمیتوان گذرواژه شخصی را تغییر داد", "Saved" : "ذخیره شد", "No user supplied" : "هیچ کاربری تعریف نشده است", "Authentication error" : "خطا در اعتبار سنجی", "Please provide an admin recovery password; otherwise, all user data will be lost." : "لطفاً رمز عبور بازیابی مدیر را ارائه دهید. در غیر این صورت ، تمام داده های کاربر از بین می رود.", "Wrong admin recovery password. Please check the password and try again." : "رمز مدیریتی بازیابی غلط است. لطفاً رمز را کنترل کرده و دوباره امتحان نمایید.", + "Backend does not support password change, but the user's encryption key was updated." : " سرور از تغییرات گذرواژه پشتیبانی نمی کند، اما کلید رمزگذاری کاربر به روز شد.", "Federated Cloud Sharing" : "اشتراک گذاری ابر فدرال", "cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably." : "cURL از نسخه (%2$s) منسوخ شده %1$s استفاده می کند. لطفا سیستم عامل خود را به روز کنید یا ویژگی هایی از قبیل %3$s قابل اعتماد کار نمی کنند.", + "Could not determine if TLS version of cURL is outdated or not because an error happened during the HTTPS request against https://nextcloud.com. Please check the Nextcloud log file for more details." : "نمی توان تعیین کرد که آیا نسخه TLS متعلق به cURL قدیمی است یا خیر. زیرا خطایی در طول درخواست HTTPS به https://nextcloud.com رخ داده است. لطفاً فایل گزارش نکستکلود را برای جزئیات بیشتر بررسی کنید.", "Invalid SMTP password." : "گذرواژه SMTP نامعتبر است.", "Email setting test" : "تست تنظیم پست الکترونیک", "Well done, %s!" : "انجام شد،%s!", diff --git a/apps/systemtags/l10n/de.js b/apps/systemtags/l10n/de.js index f5d5e3dadd2..3e573195fbd 100644 --- a/apps/systemtags/l10n/de.js +++ b/apps/systemtags/l10n/de.js @@ -43,6 +43,8 @@ OC.L10N.register( "%s (invisible)" : "%s (unsichtbar)", "<strong>System tags</strong> for a file have been modified" : "<strong>System-Tags</strong> für eine Datei wurden geändert", "Tags" : "Tags", + "All tagged %s …" : "Alle Schlagwörter %s hinzugefügt ....", + "tagged %s" : "Schlagwort %s hinzugefügt", "Collaborative tags" : "Kollaborative Tags", "Collaborative tagging functionality which shares tags among users." : "Kollaborative Tags Schlagwort-Funktionalität, welche Schlagworte unter den Benutzern teilt.", "Collaborative tagging functionality which shares tags among users. Great for teams.\n\t(If you are a provider with a multi-tenancy installation, it is advised to deactivate this app as tags are shared.)" : "Gemeinschaftliche Schlagwort-Funktionalität, welche Schlagworte unter den Benutzern teilt. Sehr gut für Gruppen.\n(Wenn Du ein Anbieter mit einer Mehrkundeninstallation bist, so ist angeraten diese App zu deaktiveren, da die Schlagworte mit allen Kunden geteilt werden.)", diff --git a/apps/systemtags/l10n/de.json b/apps/systemtags/l10n/de.json index 4e48d98e8b9..f44234a6241 100644 --- a/apps/systemtags/l10n/de.json +++ b/apps/systemtags/l10n/de.json @@ -41,6 +41,8 @@ "%s (invisible)" : "%s (unsichtbar)", "<strong>System tags</strong> for a file have been modified" : "<strong>System-Tags</strong> für eine Datei wurden geändert", "Tags" : "Tags", + "All tagged %s …" : "Alle Schlagwörter %s hinzugefügt ....", + "tagged %s" : "Schlagwort %s hinzugefügt", "Collaborative tags" : "Kollaborative Tags", "Collaborative tagging functionality which shares tags among users." : "Kollaborative Tags Schlagwort-Funktionalität, welche Schlagworte unter den Benutzern teilt.", "Collaborative tagging functionality which shares tags among users. Great for teams.\n\t(If you are a provider with a multi-tenancy installation, it is advised to deactivate this app as tags are shared.)" : "Gemeinschaftliche Schlagwort-Funktionalität, welche Schlagworte unter den Benutzern teilt. Sehr gut für Gruppen.\n(Wenn Du ein Anbieter mit einer Mehrkundeninstallation bist, so ist angeraten diese App zu deaktiveren, da die Schlagworte mit allen Kunden geteilt werden.)", diff --git a/apps/user_status/l10n/fa.js b/apps/user_status/l10n/fa.js index eceab5a6a2a..26695de3aaa 100644 --- a/apps/user_status/l10n/fa.js +++ b/apps/user_status/l10n/fa.js @@ -20,21 +20,21 @@ OC.L10N.register( "There was an error saving the status" : "مشکلی در ذخیره سازی وضعیت پیش آمده", "There was an error clearing the status" : "مشکلی در پاک کردن وضعیت پیش آمده", "No recent status changes" : "هیچ تغییر وضعیت جدیدی وجود ندارد", - "Away" : "دور", + "Away" : "بیرون", "Do not disturb" : "مزاحم نشوید", "Don't clear" : "پاک نکن", - "Today" : "Today", + "Today" : "امروز", "This week" : "این هفته", "Online" : "آنلاین", - "Invisible" : "نامرئی", + "Invisible" : "غیر قابل مشاهده", "Offline" : "آفلاین", "There was an error saving the new status" : "مشکلی در ذخیره سازی وضعیت جدید پیش آمده", "30 minutes" : "۳۰ دقیقه", - "1 hour" : "1 ساعت", + "1 hour" : "۱ ساعت", "4 hours" : "۴ ساعت", "Mute all notifications" : "خاموش کردن همه اعلانات", "Appear offline" : "نمایش آفلاین", - "Clear status message after" : "بعد از آن پیام وضعیت را پاک کن ", + "Clear status message after" : "پاک کردن پیام وضعیت بعد از", "What's your status?" : "وضعیت شما چیست؟" }, "nplurals=2; plural=(n > 1);"); diff --git a/apps/user_status/l10n/fa.json b/apps/user_status/l10n/fa.json index bf954d2cf3b..bb7906651da 100644 --- a/apps/user_status/l10n/fa.json +++ b/apps/user_status/l10n/fa.json @@ -18,21 +18,21 @@ "There was an error saving the status" : "مشکلی در ذخیره سازی وضعیت پیش آمده", "There was an error clearing the status" : "مشکلی در پاک کردن وضعیت پیش آمده", "No recent status changes" : "هیچ تغییر وضعیت جدیدی وجود ندارد", - "Away" : "دور", + "Away" : "بیرون", "Do not disturb" : "مزاحم نشوید", "Don't clear" : "پاک نکن", - "Today" : "Today", + "Today" : "امروز", "This week" : "این هفته", "Online" : "آنلاین", - "Invisible" : "نامرئی", + "Invisible" : "غیر قابل مشاهده", "Offline" : "آفلاین", "There was an error saving the new status" : "مشکلی در ذخیره سازی وضعیت جدید پیش آمده", "30 minutes" : "۳۰ دقیقه", - "1 hour" : "1 ساعت", + "1 hour" : "۱ ساعت", "4 hours" : "۴ ساعت", "Mute all notifications" : "خاموش کردن همه اعلانات", "Appear offline" : "نمایش آفلاین", - "Clear status message after" : "بعد از آن پیام وضعیت را پاک کن ", + "Clear status message after" : "پاک کردن پیام وضعیت بعد از", "What's your status?" : "وضعیت شما چیست؟" },"pluralForm" :"nplurals=2; plural=(n > 1);" }
\ No newline at end of file diff --git a/build/integration/composer.json b/build/integration/composer.json index a9ba7980064..c0e4b51ca0c 100644 --- a/build/integration/composer.json +++ b/build/integration/composer.json @@ -3,7 +3,7 @@ "phpunit/phpunit": "^9", "dms/phpunit-arraysubset-asserts": "^0.4", "behat/behat": "~3.10.0", - "guzzlehttp/guzzle": "7.4.2", + "guzzlehttp/guzzle": "7.4.4", "jarnaiz/behat-junit-formatter": "^1.3", "sabre/dav": "4.3.1", "symfony/event-dispatcher": "~5.3" diff --git a/build/integration/features/bootstrap/WebDav.php b/build/integration/features/bootstrap/WebDav.php index ae0659d5dda..680db01a260 100644 --- a/build/integration/features/bootstrap/WebDav.php +++ b/build/integration/features/bootstrap/WebDav.php @@ -32,8 +32,8 @@ * */ use GuzzleHttp\Client as GClient; +use GuzzleHttp\Message\ResponseInterface; use PHPUnit\Framework\Assert; -use Psr\Http\Message\ResponseInterface; use Sabre\DAV\Client as SClient; use Sabre\DAV\Xml\Property\ResourceType; @@ -221,6 +221,14 @@ trait WebDav { } /** + * @Then /^Image search should work$/ + */ + public function search(): void { + $this->searchFile($this->currentUser); + Assert::assertEquals(207, $this->response->getStatusCode()); + } + + /** * @Then /^Downloaded content when downloading file "([^"]*)" with range "([^"]*)" should be "([^"]*)"$/ * @param string $fileSource * @param string $range @@ -393,13 +401,12 @@ trait WebDav { return $response; } - /* Returns the elements of a report command - * @param string $user - * @param string $path + /** + * Returns the elements of a profind command * @param string $properties properties which needs to be included in the report * @param string $filterRules filter-rules to choose what needs to appear in the report */ - public function propfindFile($user, $path, $properties = '') { + public function propfindFile(string $user, string $path, string $properties = '') { $client = $this->getSabreClient($user); $body = '<?xml version="1.0" encoding="utf-8" ?> @@ -417,6 +424,104 @@ trait WebDav { return $parsedResponse; } + /** + * Returns the elements of a searc command + * @param string $properties properties which needs to be included in the report + * @param string $filterRules filter-rules to choose what needs to appear in the report + */ + public function searchFile(string $user, ?string $properties = null, ?string $scope = null, ?string $condition = null) { + $client = $this->getSabreClient($user); + + if ($properties === null) { + $properties = '<oc:fileid /> <d:getlastmodified /> <d:getetag /> <d:getcontenttype /> <d:getcontentlength /> <nc:has-preview /> <oc:favorite /> <d:resourcetype />'; + } + + if ($condition === null) { + $condition = '<d:and> + <d:or> + <d:eq> + <d:prop> + <d:getcontenttype/> + </d:prop> + <d:literal>image/png</d:literal> + </d:eq> + + <d:eq> + <d:prop> + <d:getcontenttype/> + </d:prop> + <d:literal>image/jpeg</d:literal> + </d:eq> + + <d:eq> + <d:prop> + <d:getcontenttype/> + </d:prop> + <d:literal>image/heic</d:literal> + </d:eq> + + <d:eq> + <d:prop> + <d:getcontenttype/> + </d:prop> + <d:literal>video/mp4</d:literal> + </d:eq> + + <d:eq> + <d:prop> + <d:getcontenttype/> + </d:prop> + <d:literal>video/quicktime</d:literal> + </d:eq> + </d:or> + <d:eq> + <d:prop> + <oc:owner-id/> + </d:prop> + <d:literal>' . $user . '</d:literal> + </d:eq> +</d:and>'; + } + + if ($scope === null) { + $scope = '<d:href>/files/' . $user . '</d:href><d:depth>infinity</d:depth>'; + } + + $body = '<?xml version="1.0" encoding="UTF-8"?> +<d:searchrequest xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns" xmlns:ns="https://github.com/icewind1991/SearchDAV/ns" xmlns:ocs="http://open-collaboration-services.org/ns"> + <d:basicsearch> + <d:select> + <d:prop>' . $properties . '</d:prop> + </d:select> + <d:from><d:scope>' . $scope . '</d:scope></d:from> + <d:where>' . $condition . '</d:where> + <d:orderby> + <d:order> + <d:prop><d:getlastmodified/></d:prop> + <d:descending/> + </d:order> + </d:orderby> + <d:limit> + <d:nresults>35</d:nresults> + <ns:firstresult>0</ns:firstresult> + </d:limit> + </d:basicsearch> +</d:searchrequest>'; + + try { + $this->response = $this->makeDavRequest($user, "SEARCH", '', [ + 'Content-Type' => 'text/xml' + ], $body, ''); + var_dump((string)$this->response->getBody()); + } catch (\GuzzleHttp\Exception\ServerException $e) { + // 5xx responses cause a server exception + $this->response = $e->getResponse(); + } catch (\GuzzleHttp\Exception\ClientException $e) { + // 4xx responses cause a client exception + $this->response = $e->getResponse(); + } + } + /* Returns the elements of a report command * @param string $user * @param string $path diff --git a/build/integration/features/dav-v2.feature b/build/integration/features/dav-v2.feature index 9ecce4c6bf9..5b5c835e0dd 100644 --- a/build/integration/features/dav-v2.feature +++ b/build/integration/features/dav-v2.feature @@ -80,3 +80,9 @@ Feature: dav-v2 And As an "user0" When User "user0" uploads file "data/textfile.txt" to "/testquota/asdf.txt" Then the HTTP status code should be "201" + + Scenario: Create a search query + Given using new dav path + And As an "admin" + When User "user0" uploads file "data/green-square-256.png" to "/image.png" + When Image search should work diff --git a/core/Command/Config/Import.php b/core/Command/Config/Import.php index a823c8133b9..227c909038c 100644 --- a/core/Command/Config/Import.php +++ b/core/Command/Config/Import.php @@ -169,8 +169,8 @@ class Import extends Command implements CompletionAwareInterface { * @param string $configName */ protected function checkTypeRecursively($configValue, $configName) { - if (!is_array($configValue) && !is_bool($configValue) && !is_int($configValue) && !is_string($configValue) && !is_null($configValue)) { - throw new \UnexpectedValueException('Invalid system config value for "' . $configName . '". Only arrays, bools, integers, strings and null (delete) are allowed.'); + if (!is_array($configValue) && !is_bool($configValue) && !is_int($configValue) && !is_string($configValue) && !is_null($configValue) && !is_float($configValue)) { + throw new \UnexpectedValueException('Invalid system config value for "' . $configName . '". Only arrays, bools, integers, floats, strings and null (delete) are allowed.'); } if (is_array($configValue)) { foreach ($configValue as $key => $value) { diff --git a/core/Migrations/Version25000Date20220602190540.php b/core/Migrations/Version25000Date20220602190540.php new file mode 100644 index 00000000000..3a00423570a --- /dev/null +++ b/core/Migrations/Version25000Date20220602190540.php @@ -0,0 +1,57 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2022 Vitor Mattos <vitor@php.rio> + * + * @author Vitor Mattos <vitor@php.rio> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Core\Migrations; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\DB\Types; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +class Version25000Date20220602190540 extends SimpleMigrationStep { + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $comments = $schema->getTable('comments'); + if (!$comments->hasColumn('expire_date')) { + $comments->addColumn('expire_date', Types::DATETIME, [ + 'notnull' => false, + ]); + $comments->addIndex(['expire_date'], 'expire_date'); + return $schema; + } + return null; + } +} diff --git a/core/l10n/de.js b/core/l10n/de.js index b708235c4dc..a53ff2ca073 100644 --- a/core/l10n/de.js +++ b/core/l10n/de.js @@ -353,6 +353,7 @@ OC.L10N.register( "Your client should now be connected!" : "Dein Client sollte nun verbunden sein!", "You can close this window." : "Du kannst dieses Fenster schließen.", "This share is password-protected" : "Diese Freigabe ist passwortgeschützt", + "The password is wrong or expired. Please try again or request a new one." : "Das Passwort ist falsch oder abgelaufen. Bitte versuche es erneut oder forder ein neues Passwort an.", "Please type in your email address to request a temporary password" : "Bitte gib Deine E-Mail-Adresse ein, um ein vorläufiges Passwort anzufordern", "Email address" : "E-Mail-Adresse", "Password sent!" : "Passwort wurde verschickt", diff --git a/core/l10n/de.json b/core/l10n/de.json index 53211ef762f..ada89fe7f9a 100644 --- a/core/l10n/de.json +++ b/core/l10n/de.json @@ -351,6 +351,7 @@ "Your client should now be connected!" : "Dein Client sollte nun verbunden sein!", "You can close this window." : "Du kannst dieses Fenster schließen.", "This share is password-protected" : "Diese Freigabe ist passwortgeschützt", + "The password is wrong or expired. Please try again or request a new one." : "Das Passwort ist falsch oder abgelaufen. Bitte versuche es erneut oder forder ein neues Passwort an.", "Please type in your email address to request a temporary password" : "Bitte gib Deine E-Mail-Adresse ein, um ein vorläufiges Passwort anzufordern", "Email address" : "E-Mail-Adresse", "Password sent!" : "Passwort wurde verschickt", diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 1edc39c52f2..eae31d41449 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1036,6 +1036,7 @@ return array( 'OC\\Core\\Migrations\\Version24000Date20220404230027' => $baseDir . '/core/Migrations/Version24000Date20220404230027.php', 'OC\\Core\\Migrations\\Version24000Date20220425072957' => $baseDir . '/core/Migrations/Version24000Date20220425072957.php', 'OC\\Core\\Migrations\\Version25000Date20220515204012' => $baseDir . '/core/Migrations/Version25000Date20220515204012.php', + 'OC\\Core\\Migrations\\Version25000Date20220602190540' => $baseDir . '/core/Migrations/Version25000Date20220602190540.php', 'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 2efd6effc91..8a2a72713ec 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1069,6 +1069,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Migrations\\Version24000Date20220404230027' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20220404230027.php', 'OC\\Core\\Migrations\\Version24000Date20220425072957' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20220425072957.php', 'OC\\Core\\Migrations\\Version25000Date20220515204012' => __DIR__ . '/../../..' . '/core/Migrations/Version25000Date20220515204012.php', + 'OC\\Core\\Migrations\\Version25000Date20220602190540' => __DIR__ . '/../../..' . '/core/Migrations/Version25000Date20220602190540.php', 'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php', diff --git a/lib/private/AppFramework/Bootstrap/Coordinator.php b/lib/private/AppFramework/Bootstrap/Coordinator.php index b6378830732..3ab6ac4c8b0 100644 --- a/lib/private/AppFramework/Bootstrap/Coordinator.php +++ b/lib/private/AppFramework/Bootstrap/Coordinator.php @@ -151,7 +151,7 @@ class Coordinator { */ $this->registrationContext->delegateCapabilityRegistrations($apps); $this->registrationContext->delegateCrashReporterRegistrations($apps, $this->registry); - $this->registrationContext->delegateDashboardPanelRegistrations($apps, $this->dashboardManager); + $this->registrationContext->delegateDashboardPanelRegistrations($this->dashboardManager); $this->registrationContext->delegateEventListenerRegistrations($this->eventDispatcher); $this->registrationContext->delegateContainerRegistrations($apps); $this->registrationContext->delegateMiddlewareRegistrations($apps); diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index 7b4d24036e8..8f6aff228e1 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -475,10 +475,10 @@ class RegistrationContext { /** * @param App[] $apps */ - public function delegateDashboardPanelRegistrations(array $apps, IManager $dashboardManager): void { + public function delegateDashboardPanelRegistrations(IManager $dashboardManager): void { while (($panel = array_shift($this->dashboardPanels)) !== null) { try { - $dashboardManager->lazyRegisterWidget($panel->getService()); + $dashboardManager->lazyRegisterWidget($panel->getService(), $panel->getAppId()); } catch (Throwable $e) { $appId = $panel->getAppId(); $this->logger->error("Error during dashboard registration of $appId: " . $e->getMessage(), [ diff --git a/lib/private/Comments/Comment.php b/lib/private/Comments/Comment.php index 2b338efc75f..0128dc8defd 100644 --- a/lib/private/Comments/Comment.php +++ b/lib/private/Comments/Comment.php @@ -45,6 +45,7 @@ class Comment implements IComment { 'creationDT' => null, 'latestChildDT' => null, 'reactions' => null, + 'expire_date' => null, ]; /** @@ -447,6 +448,21 @@ class Comment implements IComment { } /** + * @inheritDoc + */ + public function setExpireDate(?\DateTime $dateTime): IComment { + $this->data['expire_date'] = $dateTime; + return $this; + } + + /** + * @inheritDoc + */ + public function getExpireDate(): ?\DateTime { + return $this->data['expire_date']; + } + + /** * sets the comment data based on an array with keys as taken from the * database. * diff --git a/lib/private/Comments/Manager.php b/lib/private/Comments/Manager.php index b7532222c33..f21f9ec76b2 100644 --- a/lib/private/Comments/Manager.php +++ b/lib/private/Comments/Manager.php @@ -107,6 +107,9 @@ class Manager implements ICommentsManager { if (!is_null($data['latest_child_timestamp'])) { $data['latest_child_timestamp'] = new \DateTime($data['latest_child_timestamp']); } + if (!is_null($data['expire_date'])) { + $data['expire_date'] = new \DateTime($data['expire_date']); + } $data['children_count'] = (int)$data['children_count']; $data['reference_id'] = $data['reference_id'] ?? null; if ($this->supportReactions()) { @@ -1203,6 +1206,7 @@ class Manager implements ICommentsManager { 'latest_child_timestamp' => $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'), 'object_type' => $qb->createNamedParameter($comment->getObjectType()), 'object_id' => $qb->createNamedParameter($comment->getObjectId()), + 'expire_date' => $qb->createNamedParameter($comment->getExpireDate(), 'datetime'), ]; if ($tryWritingReferenceId) { @@ -1642,4 +1646,21 @@ class Manager implements ICommentsManager { $this->initialStateService->provideInitialState('comments', 'max-message-length', IComment::MAX_MESSAGE_LENGTH); Util::addScript('comments', 'comments-app'); } + + /** + * @inheritDoc + */ + public function deleteMessageExpiredAtObject(string $objectType, string $objectId): bool { + $qb = $this->dbConn->getQueryBuilder(); + $affectedRows = $qb->delete('comments') + ->where($qb->expr()->lt('expire_date', + $qb->createNamedParameter($this->timeFactory->getDateTime(), IQueryBuilder::PARAM_DATE))) + ->andWhere($qb->expr()->eq('object_type', $qb->createNamedParameter($objectType))) + ->andWhere($qb->expr()->eq('object_id', $qb->createNamedParameter($objectId))) + ->executeStatement(); + + $this->commentsCache = []; + + return $affectedRows > 0; + } } diff --git a/lib/private/Dashboard/Manager.php b/lib/private/Dashboard/Manager.php index 09525693b4f..2aeedf3174e 100644 --- a/lib/private/Dashboard/Manager.php +++ b/lib/private/Dashboard/Manager.php @@ -27,10 +27,11 @@ declare(strict_types=1); namespace OC\Dashboard; use InvalidArgumentException; -use OCP\AppFramework\QueryException; +use OCP\App\IAppManager; use OCP\Dashboard\IManager; use OCP\Dashboard\IWidget; -use OCP\IServerContainer; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\ContainerInterface; use Throwable; use Psr\Log\LoggerInterface; @@ -42,10 +43,10 @@ class Manager implements IManager { /** @var IWidget[] */ private $widgets = []; - /** @var IServerContainer */ - private $serverContainer; + private ContainerInterface $serverContainer; + private ?IAppManager $appManager = null; - public function __construct(IServerContainer $serverContainer) { + public function __construct(ContainerInterface $serverContainer) { $this->serverContainer = $serverContainer; } @@ -57,17 +58,25 @@ class Manager implements IManager { $this->widgets[$widget->getId()] = $widget; } - public function lazyRegisterWidget(string $widgetClass): void { - $this->lazyWidgets[] = $widgetClass; + public function lazyRegisterWidget(string $widgetClass, string $appId): void { + $this->lazyWidgets[] = ['class' => $widgetClass, 'appId' => $appId]; } public function loadLazyPanels(): void { - $classes = $this->lazyWidgets; - foreach ($classes as $class) { + if ($this->appManager === null) { + $this->appManager = $this->serverContainer->get(IAppManager::class); + } + $services = $this->lazyWidgets; + foreach ($services as $service) { + /** @psalm-suppress InvalidCatch */ try { + if (!$this->appManager->isEnabledForUser($service['appId'])) { + // all apps are registered, but some may not be enabled for the user + continue; + } /** @var IWidget $widget */ - $widget = $this->serverContainer->query($class); - } catch (QueryException $e) { + $widget = $this->serverContainer->get($service['class']); + } catch (ContainerExceptionInterface $e) { /* * There is a circular dependency between the logger and the registry, so * we can not inject it. Thus the static call. @@ -90,7 +99,7 @@ class Manager implements IManager { */ \OC::$server->get(LoggerInterface::class)->critical( 'Could not register lazy dashboard widget: ' . $e->getMessage(), - ['excepiton' => $e] + ['exception' => $e] ); } @@ -111,7 +120,7 @@ class Manager implements IManager { } catch (Throwable $e) { \OC::$server->get(LoggerInterface::class)->critical( 'Error during dashboard widget loading: ' . $e->getMessage(), - ['excepiton' => $e] + ['exception' => $e] ); } } diff --git a/lib/private/Http/Client/LocalAddressChecker.php b/lib/private/Http/Client/LocalAddressChecker.php index 2789b1b5935..c69d1007a16 100644 --- a/lib/private/Http/Client/LocalAddressChecker.php +++ b/lib/private/Http/Client/LocalAddressChecker.php @@ -66,8 +66,10 @@ class LocalAddressChecker { $host = substr($host, 1, -1); } - // Disallow localhost and local network - if ($host === 'localhost' || substr($host, -6) === '.local' || substr($host, -10) === '.localhost') { + // Disallow local network top-level domains from RFC 6762 + $localTopLevelDomains = ['local','localhost','intranet','internal','private','corp','home','lan']; + $topLevelDomain = substr((strrchr($host, '.') ?: ''), 1); + if (in_array($topLevelDomain, $localTopLevelDomains)) { $this->logger->warning("Host $host was not connected to because it violates local access rules"); throw new LocalServerException('Host violates local access rules'); } diff --git a/lib/public/Comments/IComment.php b/lib/public/Comments/IComment.php index 8465eaf49f4..44d294bb07c 100644 --- a/lib/public/Comments/IComment.php +++ b/lib/public/Comments/IComment.php @@ -299,4 +299,21 @@ interface IComment { * @since 24.0.0 */ public function setReactions(?array $reactions): IComment; + + /** + * Set message expire date + * + * @param \DateTime|null $dateTime + * @return IComment + * @since 25.0.0 + */ + public function setExpireDate(?\DateTime $dateTime): IComment; + + /** + * Get message expire date + * + * @return ?\DateTime + * @since 25.0.0 + */ + public function getExpireDate(): ?\DateTime; } diff --git a/lib/public/Comments/ICommentsManager.php b/lib/public/Comments/ICommentsManager.php index c34bd4718cc..814ca3e8f9c 100644 --- a/lib/public/Comments/ICommentsManager.php +++ b/lib/public/Comments/ICommentsManager.php @@ -482,4 +482,15 @@ interface ICommentsManager { * @since 21.0.0 */ public function load(): void; + + /** + * Delete comments with field expire_date less than current date + * Only will delete the message related with the object. + * + * @param string $objectType the object type (e.g. 'files') + * @param string $objectId e.g. the file id + * @return boolean true if at least one row was deleted + * @since 25.0.0 + */ + public function deleteMessageExpiredAtObject(string $objectType, string $objectId): bool; } diff --git a/lib/public/Dashboard/IManager.php b/lib/public/Dashboard/IManager.php index 396624876ef..d9e73c89eb9 100644 --- a/lib/public/Dashboard/IManager.php +++ b/lib/public/Dashboard/IManager.php @@ -36,7 +36,7 @@ interface IManager { * @param string $widgetClass * @since 20.0.0 */ - public function lazyRegisterWidget(string $widgetClass): void; + public function lazyRegisterWidget(string $widgetClass, string $appId): void; /** * @since 20.0.0 diff --git a/tests/lib/Comments/FakeManager.php b/tests/lib/Comments/FakeManager.php index 5406df96a96..0d615fd2632 100644 --- a/tests/lib/Comments/FakeManager.php +++ b/tests/lib/Comments/FakeManager.php @@ -141,4 +141,8 @@ class FakeManager implements ICommentsManager { public function getLastCommentDateByActor(string $objectType, string $objectId, string $verb, string $actorType, array $actors): array { return []; } + + public function deleteMessageExpiredAtObject(string $objectType, string $objectId): bool { + return true; + } } diff --git a/tests/lib/Comments/ManagerTest.php b/tests/lib/Comments/ManagerTest.php index 6bcd0dec8ed..7c6971476c7 100644 --- a/tests/lib/Comments/ManagerTest.php +++ b/tests/lib/Comments/ManagerTest.php @@ -14,6 +14,7 @@ use OCP\IConfig; use OCP\IDBConnection; use OCP\IInitialStateService; use OCP\IUser; +use OCP\Server; use Psr\Log\LoggerInterface; use Test\TestCase; @@ -37,7 +38,7 @@ class ManagerTest extends TestCase { $this->connection->prepare($sql)->execute(); } - protected function addDatabaseEntry($parentId, $topmostParentId, $creationDT = null, $latestChildDT = null, $objectId = null) { + protected function addDatabaseEntry($parentId, $topmostParentId, $creationDT = null, $latestChildDT = null, $objectId = null, $expireDate = null) { if (is_null($creationDT)) { $creationDT = new \DateTime(); } @@ -63,6 +64,7 @@ class ManagerTest extends TestCase { 'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, 'datetime'), 'object_type' => $qb->createNamedParameter('files'), 'object_id' => $qb->createNamedParameter($objectId), + 'expire_date' => $qb->createNamedParameter($expireDate, 'datetime'), ]) ->execute(); @@ -701,6 +703,51 @@ class ManagerTest extends TestCase { $this->assertTrue($wasSuccessful); } + public function testDeleteMessageExpiredAtObject(): void { + $ids = []; + $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('+2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('+2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('+2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('-2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('-2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('-2 hours')); + + $manager = new Manager( + $this->connection, + $this->createMock(LoggerInterface::class), + $this->createMock(IConfig::class), + Server::get(ITimeFactory::class), + new EmojiHelper($this->connection), + $this->createMock(IInitialStateService::class) + ); + + // just to make sure they are really set, with correct actor data + $comment = $manager->get((string) $ids[1]); + $this->assertSame($comment->getObjectType(), 'files'); + $this->assertSame($comment->getObjectId(), 'file64'); + + $deleted = $manager->deleteMessageExpiredAtObject('files', 'file64'); + $this->assertTrue($deleted); + + $deleted = 0; + $exists = 0; + foreach ($ids as $id) { + try { + $manager->get((string) $id); + $exists++; + } catch (NotFoundException $e) { + $deleted++; + } + } + $this->assertSame($exists, 3); + $this->assertSame($deleted, 3); + + // actor info is gone from DB, but when database interaction is alright, + // we still expect to get true back + $deleted = $manager->deleteMessageExpiredAtObject('files', 'file64'); + $this->assertFalse($deleted); + } + public function testSetMarkRead() { /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ $user = $this->createMock(IUser::class); |