Merge branch 'master' into make-sass-command-compatible-with-macos

Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
pull/44452/head
Marco 2 months ago committed by nextcloud-command
commit e8da4a345f

@ -4,7 +4,7 @@
Make sure you have the [VSCode DevContainer](https://code.visualstudio.com/docs/devcontainers/containers) extensions installed. If you open the project, VSCode will ask you if you want to open it inside of the DevContainer. If that's not the case, use <kbd>F1</kbd>&rarr;*Dev Containers: Open Folder in Container*.
Alternatively open the project directly in [GitHub Codespaces](https://github.com/features/codespaces).
Alternatively open the project directly in [GitHub Codespaces](https://github.com/codespaces/new?hide_repo_select=true&ref=master&repo=60243197&skip_quickstart=true).
That's already it. Everything else will be configured automatically by the Containers startup routine.
@ -69,4 +69,4 @@ any other user.
The Apache webserver is already configured to automatically try to connect to a debugger process
listening on port `9003`. To start the VSCode debugger process, use the delivered debug profile `Listen for XDebug`.
After you started the VSCode debugger, just navigate to the appropriate Nextcloud URL to get your
debug hits.
debug hits.

@ -34,4 +34,4 @@ jobs:
- name: Checking if ${{ env.server_major }} is EOL
run: |
php -r 'echo json_encode(require_once "config.php");' | jq --arg version "${{ env.server_major }}" '.stable[$version]["100"].eol // .beta[$version]["100"].eol' | grep --silent -i 'false'
php -r 'echo json_encode(require_once "config.php");' | jq --arg version "${{ env.server_major }}" '.stable[$version]["100"].eol // .beta[$version]["100"].eol // "NotEOL"' | grep -q "NotEOL"

@ -2,6 +2,6 @@ OC.L10N.register(
"admin_audit",
{
"Auditing / Logging" : "Audit / Journalisation",
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Fournit des capacités de journalisation pour Nextcloud, telles que la journalisation des accès aux fichiers ou d'autres actions sensibles."
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Fournit des capacités de journalisation pour Nextcloud, telles que la journalisation des accès aux fichiers ou dautres actions sensibles."
},
"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

@ -1,5 +1,5 @@
{ "translations": {
"Auditing / Logging" : "Audit / Journalisation",
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Fournit des capacités de journalisation pour Nextcloud, telles que la journalisation des accès aux fichiers ou d'autres actions sensibles."
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Fournit des capacités de journalisation pour Nextcloud, telles que la journalisation des accès aux fichiers ou dautres actions sensibles."
},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

@ -3,6 +3,6 @@ OC.L10N.register(
{
"Cloud Federation API" : "API Cloud Federation",
"Enable clouds to communicate with each other and exchange data" : "Permettre aux clouds de communiquer entre eux et d'échanger des données",
"The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "L'API Cloud Federation permet à diverses instances Nextcloud de communiquer entre elles et d'échanger des données."
"The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "LAPI Cloud Federation permet à diverses instances Nextcloud de communiquer entre elles et déchanger des données."
},
"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

@ -1,6 +1,6 @@
{ "translations": {
"Cloud Federation API" : "API Cloud Federation",
"Enable clouds to communicate with each other and exchange data" : "Permettre aux clouds de communiquer entre eux et d'échanger des données",
"The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "L'API Cloud Federation permet à diverses instances Nextcloud de communiquer entre elles et d'échanger des données."
"The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "LAPI Cloud Federation permet à diverses instances Nextcloud de communiquer entre elles et déchanger des données."
},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

@ -32,6 +32,8 @@ OC.L10N.register(
"An error occurred while trying to delete the comment" : "Ocurrió un error intentando borrar el comentario",
"An error occurred while trying to create the comment" : "Ocurrió un error al intentar crear el comentario",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Fue mencionado en \"{file}\", en un comentario realizado por un usuario que ha sido eliminado",
"_%n unread comment_::_%n unread comments_" : ["%n comentarios sin leer","%n comentarios sin leer","%n comentarios sin leer"]
"_%n unread comment_::_%n unread comments_" : ["%n comentarios sin leer","%n comentarios sin leer","%n comentarios sin leer"],
"Write a message …" : "Escriba un mensaje …",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "\"@\" para menciones, \":\" para emoticonos, \"/\" para selector inteligente"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

@ -30,6 +30,8 @@
"An error occurred while trying to delete the comment" : "Ocurrió un error intentando borrar el comentario",
"An error occurred while trying to create the comment" : "Ocurrió un error al intentar crear el comentario",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Fue mencionado en \"{file}\", en un comentario realizado por un usuario que ha sido eliminado",
"_%n unread comment_::_%n unread comments_" : ["%n comentarios sin leer","%n comentarios sin leer","%n comentarios sin leer"]
"_%n unread comment_::_%n unread comments_" : ["%n comentarios sin leer","%n comentarios sin leer","%n comentarios sin leer"],
"Write a message …" : "Escriba un mensaje …",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "\"@\" para menciones, \":\" para emoticonos, \"/\" para selector inteligente"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

@ -9,11 +9,14 @@ OC.L10N.register(
"%1$s commented on %2$s" : "%1$s-ek %2$s-en iruzkindu du",
"{author} commented on {file}" : "{author}-(e)k {file}-en iruzkina egin du",
"<strong>Comments</strong> for files" : "Fitxategientzako <strong>iruzkinak</strong>",
"You were mentioned on \"{file}\", in a comment by an account that has since been deleted" : "\"{file}\"-n aipatu zaituzte, dagoeneko ezabatu den kontu baten iruzkin batean",
"{user} mentioned you in a comment on \"{file}\"" : "{user} erabiltzaileak aipatu zaitu \"{file}\"-eko iruzkin batean",
"Files app plugin to add comments to files" : "Fitxategiak aplikazioko plugina, fitxategiei iruzkinak gehitzeko",
"Edit comment" : "Editatu iruzkina",
"Delete comment" : "Ezabatu iruzkina",
"Cancel edit" : "Utzi editatzeari",
"New comment" : "Iruzkin berria",
"Write a comment …" : "Idatzi iruzkin bat ...",
"Post comment" : "Argitaratu iruzkina",
"@ for mentions, : for emoji, / for smart picker" : "@ aipamenetarako, : emojientzako, / hautatzaile adimentsurako",
"Could not reload comments" : "Ezin izan dira iruzkinak freskatu",
@ -30,6 +33,7 @@ OC.L10N.register(
"An error occurred while trying to create the comment" : "Errorea gertatu da iruzkina sortzen saiatzean",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "\"{file}\"-n aipatu zaituzte, dagoeneko ezabatu den erabiltzaile baten iruzkin batean",
"_%n unread comment_::_%n unread comments_" : ["iruzkin %n irakurri gabe","%n iruzkin irakurri gabe"],
"Write a message …" : "Idatzi mezu bat ...",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "\"@\" aipamenetarako, \":\" emojientzako, \"/\" hautatzaile adimentsurako"
},
"nplurals=2; plural=(n != 1);");

@ -7,11 +7,14 @@
"%1$s commented on %2$s" : "%1$s-ek %2$s-en iruzkindu du",
"{author} commented on {file}" : "{author}-(e)k {file}-en iruzkina egin du",
"<strong>Comments</strong> for files" : "Fitxategientzako <strong>iruzkinak</strong>",
"You were mentioned on \"{file}\", in a comment by an account that has since been deleted" : "\"{file}\"-n aipatu zaituzte, dagoeneko ezabatu den kontu baten iruzkin batean",
"{user} mentioned you in a comment on \"{file}\"" : "{user} erabiltzaileak aipatu zaitu \"{file}\"-eko iruzkin batean",
"Files app plugin to add comments to files" : "Fitxategiak aplikazioko plugina, fitxategiei iruzkinak gehitzeko",
"Edit comment" : "Editatu iruzkina",
"Delete comment" : "Ezabatu iruzkina",
"Cancel edit" : "Utzi editatzeari",
"New comment" : "Iruzkin berria",
"Write a comment …" : "Idatzi iruzkin bat ...",
"Post comment" : "Argitaratu iruzkina",
"@ for mentions, : for emoji, / for smart picker" : "@ aipamenetarako, : emojientzako, / hautatzaile adimentsurako",
"Could not reload comments" : "Ezin izan dira iruzkinak freskatu",
@ -28,6 +31,7 @@
"An error occurred while trying to create the comment" : "Errorea gertatu da iruzkina sortzen saiatzean",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "\"{file}\"-n aipatu zaituzte, dagoeneko ezabatu den erabiltzaile baten iruzkin batean",
"_%n unread comment_::_%n unread comments_" : ["iruzkin %n irakurri gabe","%n iruzkin irakurri gabe"],
"Write a message …" : "Idatzi mezu bat ...",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "\"@\" aipamenetarako, \":\" emojientzako, \"/\" hautatzaile adimentsurako"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

@ -10,7 +10,7 @@ OC.L10N.register(
"{author} commented on {file}" : "{author} a commenté sur {file}",
"<strong>Comments</strong> for files" : "<strong>Commentaires</strong> sur les fichiers",
"You were mentioned on \"{file}\", in a comment by an account that has since been deleted" : "Vous avez été mentionné sur « {file} », dans un commentaire par un compte qui depuis a été supprimé",
"{user} mentioned you in a comment on \"{file}\"" : "{user} vous a mentionné dans un commentaire sur \"{file}\"",
"{user} mentioned you in a comment on \"{file}\"" : "{user} vous a mentionné dans un commentaire sur « {file} »",
"Files app plugin to add comments to files" : "Plugin Fichiers app pour ajouter des commentaires aux fichiers",
"Edit comment" : "Modifier le commentaire",
"Delete comment" : "Supprimer le commentaire",
@ -31,8 +31,8 @@ OC.L10N.register(
"Comment deleted" : "Commentaire supprimé",
"An error occurred while trying to delete the comment" : "Une erreur s'est produite lors de la tentative de suppression du commentaire",
"An error occurred while trying to create the comment" : "Une erreur s'est produite lors de la tentative de création du commentaire",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Vous avez été mentionné sur \"{file}\", dans un commentaire par un utilisateur qui depuis a été supprimé",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Vous avez été mentionné sur « {file} », dans un commentaire par un utilisateur qui depuis a été supprimé",
"_%n unread comment_::_%n unread comments_" : ["%n commentaire non lu","%n commentaires non lus","%n commentaires non lus"],
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "\"@\" pour les mentions, \":\" pour les émojis, \"/\" pour le sélecteur intelligent"
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "« @ » pour les mentions, « : »pour les émojis, « / » pour le sélecteur intelligent"
},
"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

@ -8,7 +8,7 @@
"{author} commented on {file}" : "{author} a commenté sur {file}",
"<strong>Comments</strong> for files" : "<strong>Commentaires</strong> sur les fichiers",
"You were mentioned on \"{file}\", in a comment by an account that has since been deleted" : "Vous avez été mentionné sur « {file} », dans un commentaire par un compte qui depuis a été supprimé",
"{user} mentioned you in a comment on \"{file}\"" : "{user} vous a mentionné dans un commentaire sur \"{file}\"",
"{user} mentioned you in a comment on \"{file}\"" : "{user} vous a mentionné dans un commentaire sur « {file} »",
"Files app plugin to add comments to files" : "Plugin Fichiers app pour ajouter des commentaires aux fichiers",
"Edit comment" : "Modifier le commentaire",
"Delete comment" : "Supprimer le commentaire",
@ -29,8 +29,8 @@
"Comment deleted" : "Commentaire supprimé",
"An error occurred while trying to delete the comment" : "Une erreur s'est produite lors de la tentative de suppression du commentaire",
"An error occurred while trying to create the comment" : "Une erreur s'est produite lors de la tentative de création du commentaire",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Vous avez été mentionné sur \"{file}\", dans un commentaire par un utilisateur qui depuis a été supprimé",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Vous avez été mentionné sur « {file} », dans un commentaire par un utilisateur qui depuis a été supprimé",
"_%n unread comment_::_%n unread comments_" : ["%n commentaire non lu","%n commentaires non lus","%n commentaires non lus"],
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "\"@\" pour les mentions, \":\" pour les émojis, \"/\" pour le sélecteur intelligent"
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "« @ » pour les mentions, « : »pour les émojis, « / » pour le sélecteur intelligent"
},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

@ -32,6 +32,8 @@ OC.L10N.register(
"An error occurred while trying to delete the comment" : "Ocorreu um erro ao tentar excluir o comentário",
"An error occurred while trying to create the comment" : "Ocorreu um erro ao tentar criar o comentário",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Você foi mencionado em \"{file}\", em um comentário de um usuário que já foi excluído",
"_%n unread comment_::_%n unread comments_" : ["%n comentários não lidos","%n comentários não lidos","%n comentários não lidos"]
"_%n unread comment_::_%n unread comments_" : ["%n comentários não lidos","%n comentários não lidos","%n comentários não lidos"],
"Write a message …" : "Escreve uma mensagem …",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "\"@\" para menções, \":\" para emoji, \"/\" para seletor inteligente"
},
"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

@ -30,6 +30,8 @@
"An error occurred while trying to delete the comment" : "Ocorreu um erro ao tentar excluir o comentário",
"An error occurred while trying to create the comment" : "Ocorreu um erro ao tentar criar o comentário",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Você foi mencionado em \"{file}\", em um comentário de um usuário que já foi excluído",
"_%n unread comment_::_%n unread comments_" : ["%n comentários não lidos","%n comentários não lidos","%n comentários não lidos"]
"_%n unread comment_::_%n unread comments_" : ["%n comentários não lidos","%n comentários não lidos","%n comentários não lidos"],
"Write a message …" : "Escreve uma mensagem …",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "\"@\" para menções, \":\" para emoji, \"/\" para seletor inteligente"
},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

@ -32,6 +32,8 @@ OC.L10N.register(
"An error occurred while trying to delete the comment" : "Vyskytla sa chyba pri mazaní komentára",
"An error occurred while trying to create the comment" : "Vyskytla sa chyba pri vytváraní komentára",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Boli ste spomenutý v \"{file}\", v komentári používateľom ktorý bol už vymazaný",
"_%n unread comment_::_%n unread comments_" : ["%n neprečítaný komentár","%n neprečítaných komentárov","%n neprečítaných komentárov","%n neprečítaných komentárov"]
"_%n unread comment_::_%n unread comments_" : ["%n neprečítaný komentár","%n neprečítaných komentárov","%n neprečítaných komentárov","%n neprečítaných komentárov"],
"Write a message …" : "Napísať správu …",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "\"@\" pre spomienky, \":\" pre emotikony, \"/\" pre inteligentný výber"
},
"nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);");

@ -30,6 +30,8 @@
"An error occurred while trying to delete the comment" : "Vyskytla sa chyba pri mazaní komentára",
"An error occurred while trying to create the comment" : "Vyskytla sa chyba pri vytváraní komentára",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Boli ste spomenutý v \"{file}\", v komentári používateľom ktorý bol už vymazaný",
"_%n unread comment_::_%n unread comments_" : ["%n neprečítaný komentár","%n neprečítaných komentárov","%n neprečítaných komentárov","%n neprečítaných komentárov"]
"_%n unread comment_::_%n unread comments_" : ["%n neprečítaný komentár","%n neprečítaných komentárov","%n neprečítaných komentárov","%n neprečítaných komentárov"],
"Write a message …" : "Napísať správu …",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "\"@\" pre spomienky, \":\" pre emotikony, \"/\" pre inteligentný výber"
},"pluralForm" :"nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);"
}

@ -33,6 +33,7 @@ OC.L10N.register(
"An error occurred while trying to create the comment" : "Грешка приликом покушаја креирања коментара",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Поменути сте за „{file}”, у коментару корисника које је од тада обрисан",
"_%n unread comment_::_%n unread comments_" : ["%nнепрочитани коментар","%nнепрочитана коментара ","%n непрочитаних коментара"],
"Write a message …" : "Напиши поруку…",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "„@” за помињања, „:” за емођи, „/” за паметни бирач"
},
"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);");

@ -31,6 +31,7 @@
"An error occurred while trying to create the comment" : "Грешка приликом покушаја креирања коментара",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Поменути сте за „{file}”, у коментару корисника које је од тада обрисан",
"_%n unread comment_::_%n unread comments_" : ["%nнепрочитани коментар","%nнепрочитана коментара ","%n непрочитаних коментара"],
"Write a message …" : "Напиши поруку…",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "„@” за помињања, „:” за емођи, „/” за паметни бирач"
},"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
}

@ -33,6 +33,7 @@ OC.L10N.register(
"An error occurred while trying to create the comment" : "Ett fel uppstod när du försökte skapa kommentaren",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Du har nämnts i \"{file}\", i en kommentar av en användare som sedan har blivit borttagen",
"_%n unread comment_::_%n unread comments_" : ["%n oläst kommentar","%n olästa kommentarer"],
"Write a message …" : "Skriv ett meddelande …",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "\"@\" för omnämnanden, \":\" för emoji, \"/\" för smart picker"
},
"nplurals=2; plural=(n != 1);");

@ -31,6 +31,7 @@
"An error occurred while trying to create the comment" : "Ett fel uppstod när du försökte skapa kommentaren",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "Du har nämnts i \"{file}\", i en kommentar av en användare som sedan har blivit borttagen",
"_%n unread comment_::_%n unread comments_" : ["%n oläst kommentar","%n olästa kommentarer"],
"Write a message …" : "Skriv ett meddelande …",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "\"@\" för omnämnanden, \":\" för emoji, \"/\" för smart picker"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

@ -33,6 +33,7 @@ OC.L10N.register(
"An error occurred while trying to create the comment" : "Yorum eklenmeye çalışılırken bir sorun çıktı",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "\"{file}\" hakkında bir yorumda silinmiş bir kullanıcı tarafından anıldınız",
"_%n unread comment_::_%n unread comments_" : ["%n okunmamış yorum","%n okunmamış yorum"],
"Write a message …" : "Bir ileti yazın…",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "Anmalar için \"@\" , emojiler için \":\" akıllı seçici için \"/\""
},
"nplurals=2; plural=(n > 1);");

@ -31,6 +31,7 @@
"An error occurred while trying to create the comment" : "Yorum eklenmeye çalışılırken bir sorun çıktı",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "\"{file}\" hakkında bir yorumda silinmiş bir kullanıcı tarafından anıldınız",
"_%n unread comment_::_%n unread comments_" : ["%n okunmamış yorum","%n okunmamış yorum"],
"Write a message …" : "Bir ileti yazın…",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "Anmalar için \"@\" , emojiler için \":\" akıllı seçici için \"/\""
},"pluralForm" :"nplurals=2; plural=(n > 1);"
}

@ -9,6 +9,7 @@ OC.L10N.register(
"%1$s commented on %2$s" : "%1$s 已對 %2$s 留言",
"{author} commented on {file}" : "{author} 已對 {file} 留言",
"<strong>Comments</strong> for files" : "檔案的<strong>留言</strong>",
"You were mentioned on \"{file}\", in a comment by an account that has since been deleted" : "一個已刪除的帳號在「{file}」的留言中提及您。",
"{user} mentioned you in a comment on \"{file}\"" : "{user} 在「{file}」的留言中提到您",
"Files app plugin to add comments to files" : "用於對檔案加入留言的檔案應用程式擴充元件",
"Edit comment" : "編輯留言",
@ -32,6 +33,7 @@ OC.L10N.register(
"An error occurred while trying to create the comment" : "嘗試建立留言時發生錯誤",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "一個已被刪除的使用者在「{file}」的留言中提到您",
"_%n unread comment_::_%n unread comments_" : ["%n 則未讀留言"],
"Write a message …" : "撰寫訊息……",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "「@」表示提及、「:」表示表情符號、「/」表示智慧型選取程式"
},
"nplurals=1; plural=0;");

@ -7,6 +7,7 @@
"%1$s commented on %2$s" : "%1$s 已對 %2$s 留言",
"{author} commented on {file}" : "{author} 已對 {file} 留言",
"<strong>Comments</strong> for files" : "檔案的<strong>留言</strong>",
"You were mentioned on \"{file}\", in a comment by an account that has since been deleted" : "一個已刪除的帳號在「{file}」的留言中提及您。",
"{user} mentioned you in a comment on \"{file}\"" : "{user} 在「{file}」的留言中提到您",
"Files app plugin to add comments to files" : "用於對檔案加入留言的檔案應用程式擴充元件",
"Edit comment" : "編輯留言",
@ -30,6 +31,7 @@
"An error occurred while trying to create the comment" : "嘗試建立留言時發生錯誤",
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "一個已被刪除的使用者在「{file}」的留言中提到您",
"_%n unread comment_::_%n unread comments_" : ["%n 則未讀留言"],
"Write a message …" : "撰寫訊息……",
"\"@\" for mentions, \":\" for emoji, \"/\" for smart picker" : "「@」表示提及、「:」表示表情符號、「/」表示智慧型選取程式"
},"pluralForm" :"nplurals=1; plural=0;"
}

@ -3,15 +3,16 @@ OC.L10N.register(
{
"Dashboard" : "Tableau de bord",
"Dashboard app" : "App Tableau de bord",
"\"{title} icon\"" : "\"Icône {title}\"",
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an overview of your upcoming appointments, urgent emails, chat messages, incoming tickets, latest tweets and much more! People can add the widgets they like and change the background to their liking." : "Commencez votre journée en étant informé\n\nLe tableau de bord Nextcloud est votre point de départ de la journée, vous donnant un aperçu de vos rendez-vous à venir, des courriels urgents, des messages de discussion, des tickets entrants, des derniers tweets et bien plus encore ! Les personnes peuvent ajouter les widgets quils souhaitent et changer larrière-plan à leur guise.",
"\"{title} icon\"" :  Icône {title} »",
"Customize" : "Personnaliser",
"Edit widgets" : "Modifier les widgets",
"Get more widgets from the App Store" : "Obtenez plus de widgets depuis la Boutique d'applications",
"Get more widgets from the App Store" : "Obtenez plus de widgets depuis le magasin dapplications",
"Weather service" : "Service météo",
"For your privacy, the weather data is requested by your Nextcloud server on your behalf so the weather service receives no personal information." : "Afin de protéger votre vie privée, les données météorologiques sont demandées par votre serveur Nextcloud à votre place afin que le service météo ne reçoive aucune information personnelle.",
"Weather data from Met.no" : "Données météo fournies par Met.no",
"geocoding with Nominatim" : "Géocodage avec Nominatim",
"elevation data from OpenTopoData" : "Données d'altitude provenant d'OpenTopoData",
"elevation data from OpenTopoData" : "Données daltitude provenant dOpenTopoData",
"Weather" : "Météo",
"Status" : "Statut",
"Good morning" : "Bonjour",
@ -22,7 +23,7 @@ OC.L10N.register(
"Good evening, {name}" : "Bonsoir {name}",
"Hello" : "Bonjour",
"Hello, {name}" : "Bonjour {name}",
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an\noverview of your upcoming appointments, urgent emails, chat messages,\nincoming tickets, latest tweets and much more! Users can add the widgets\nthey like and change the background to their liking." : "Commencez votre journée en étant informé\n\nLe tableau de bord Nextcloud est votre point de départ de la journée, vous donnant un\naperçu de vos rendez-vous à venir, des e-mails urgents, des messages de tchat,\ndes tickets entrants, des derniers tweets et bien plus encore ! Les utilisateurs peuvent ajouter les widgets\nqu'ils souhaitent et modifier l'arrière-plan à leur guise.",
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an overview of your upcoming appointments, urgent emails, chat messages, incoming tickets, latest tweets and much more! Users can add the widgets they like and change the background to their liking." : "Commencez votre journée en étant informé\n\nLe tableau de bord Nextcloud est votre point de départ de la journée, vous donnant un aperçu de vos rendez-vous à venir, des emails urgents, des messages de discussion, des tickets entrants, des derniers tweets et bien plus encore ! Les utilisateurs peuvent ajouter les widgets qu'ils souhaitent et changer l'arrière-plan à leur guise."
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an\noverview of your upcoming appointments, urgent emails, chat messages,\nincoming tickets, latest tweets and much more! Users can add the widgets\nthey like and change the background to their liking." : "Commencez votre journée en étant informé\n\nLe tableau de bord Nextcloud est votre point de départ de la journée, vous donnant un\naperçu de vos rendez-vous à venir, des courriels urgents, des messages de discussion,\ndes tickets entrants, des derniers tweets et bien plus encore ! Les utilisateurs peuvent ajouter les widgets\nquils souhaitent et modifier larrière-plan à leur guise.",
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an overview of your upcoming appointments, urgent emails, chat messages, incoming tickets, latest tweets and much more! Users can add the widgets they like and change the background to their liking." : "Commencez votre journée en étant informé\n\nLe tableau de bord Nextcloud est votre point de départ de la journée, vous donnant un aperçu de vos rendez-vous à venir, des courriels urgents, des messages de discussion, des tickets entrants, des derniers tweets et bien plus encore ! Les utilisateurs peuvent ajouter les widgets quils souhaitent et changer larrière-plan à leur guise."
},
"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

@ -1,15 +1,16 @@
{ "translations": {
"Dashboard" : "Tableau de bord",
"Dashboard app" : "App Tableau de bord",
"\"{title} icon\"" : "\"Icône {title}\"",
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an overview of your upcoming appointments, urgent emails, chat messages, incoming tickets, latest tweets and much more! People can add the widgets they like and change the background to their liking." : "Commencez votre journée en étant informé\n\nLe tableau de bord Nextcloud est votre point de départ de la journée, vous donnant un aperçu de vos rendez-vous à venir, des courriels urgents, des messages de discussion, des tickets entrants, des derniers tweets et bien plus encore ! Les personnes peuvent ajouter les widgets quils souhaitent et changer larrière-plan à leur guise.",
"\"{title} icon\"" :  Icône {title} »",
"Customize" : "Personnaliser",
"Edit widgets" : "Modifier les widgets",
"Get more widgets from the App Store" : "Obtenez plus de widgets depuis la Boutique d'applications",
"Get more widgets from the App Store" : "Obtenez plus de widgets depuis le magasin dapplications",
"Weather service" : "Service météo",
"For your privacy, the weather data is requested by your Nextcloud server on your behalf so the weather service receives no personal information." : "Afin de protéger votre vie privée, les données météorologiques sont demandées par votre serveur Nextcloud à votre place afin que le service météo ne reçoive aucune information personnelle.",
"Weather data from Met.no" : "Données météo fournies par Met.no",
"geocoding with Nominatim" : "Géocodage avec Nominatim",
"elevation data from OpenTopoData" : "Données d'altitude provenant d'OpenTopoData",
"elevation data from OpenTopoData" : "Données daltitude provenant dOpenTopoData",
"Weather" : "Météo",
"Status" : "Statut",
"Good morning" : "Bonjour",
@ -20,7 +21,7 @@
"Good evening, {name}" : "Bonsoir {name}",
"Hello" : "Bonjour",
"Hello, {name}" : "Bonjour {name}",
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an\noverview of your upcoming appointments, urgent emails, chat messages,\nincoming tickets, latest tweets and much more! Users can add the widgets\nthey like and change the background to their liking." : "Commencez votre journée en étant informé\n\nLe tableau de bord Nextcloud est votre point de départ de la journée, vous donnant un\naperçu de vos rendez-vous à venir, des e-mails urgents, des messages de tchat,\ndes tickets entrants, des derniers tweets et bien plus encore ! Les utilisateurs peuvent ajouter les widgets\nqu'ils souhaitent et modifier l'arrière-plan à leur guise.",
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an overview of your upcoming appointments, urgent emails, chat messages, incoming tickets, latest tweets and much more! Users can add the widgets they like and change the background to their liking." : "Commencez votre journée en étant informé\n\nLe tableau de bord Nextcloud est votre point de départ de la journée, vous donnant un aperçu de vos rendez-vous à venir, des emails urgents, des messages de discussion, des tickets entrants, des derniers tweets et bien plus encore ! Les utilisateurs peuvent ajouter les widgets qu'ils souhaitent et changer l'arrière-plan à leur guise."
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an\noverview of your upcoming appointments, urgent emails, chat messages,\nincoming tickets, latest tweets and much more! Users can add the widgets\nthey like and change the background to their liking." : "Commencez votre journée en étant informé\n\nLe tableau de bord Nextcloud est votre point de départ de la journée, vous donnant un\naperçu de vos rendez-vous à venir, des courriels urgents, des messages de discussion,\ndes tickets entrants, des derniers tweets et bien plus encore ! Les utilisateurs peuvent ajouter les widgets\nquils souhaitent et modifier larrière-plan à leur guise.",
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an overview of your upcoming appointments, urgent emails, chat messages, incoming tickets, latest tweets and much more! Users can add the widgets they like and change the background to their liking." : "Commencez votre journée en étant informé\n\nLe tableau de bord Nextcloud est votre point de départ de la journée, vous donnant un aperçu de vos rendez-vous à venir, des courriels urgents, des messages de discussion, des tickets entrants, des derniers tweets et bien plus encore ! Les utilisateurs peuvent ajouter les widgets quils souhaitent et changer larrière-plan à leur guise."
},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

@ -5,7 +5,7 @@
<name>WebDAV</name>
<summary>WebDAV endpoint</summary>
<description>WebDAV endpoint</description>
<version>1.30.0</version>
<version>1.30.1</version>
<licence>agpl</licence>
<author>owncloud.org</author>
<namespace>DAV</namespace>

@ -315,6 +315,7 @@ return array(
'OCA\\DAV\\Migration\\Version1017Date20210216083742' => $baseDir . '/../lib/Migration/Version1017Date20210216083742.php',
'OCA\\DAV\\Migration\\Version1018Date20210312100735' => $baseDir . '/../lib/Migration/Version1018Date20210312100735.php',
'OCA\\DAV\\Migration\\Version1024Date20211221144219' => $baseDir . '/../lib/Migration/Version1024Date20211221144219.php',
'OCA\\DAV\\Migration\\Version1025Date20240308063933' => $baseDir . '/../lib/Migration/Version1025Date20240308063933.php',
'OCA\\DAV\\Migration\\Version1027Date20230504122946' => $baseDir . '/../lib/Migration/Version1027Date20230504122946.php',
'OCA\\DAV\\Migration\\Version1029Date20221114151721' => $baseDir . '/../lib/Migration/Version1029Date20221114151721.php',
'OCA\\DAV\\Migration\\Version1029Date20231004091403' => $baseDir . '/../lib/Migration/Version1029Date20231004091403.php',

@ -330,6 +330,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\Version1017Date20210216083742' => __DIR__ . '/..' . '/../lib/Migration/Version1017Date20210216083742.php',
'OCA\\DAV\\Migration\\Version1018Date20210312100735' => __DIR__ . '/..' . '/../lib/Migration/Version1018Date20210312100735.php',
'OCA\\DAV\\Migration\\Version1024Date20211221144219' => __DIR__ . '/..' . '/../lib/Migration/Version1024Date20211221144219.php',
'OCA\\DAV\\Migration\\Version1025Date20240308063933' => __DIR__ . '/..' . '/../lib/Migration/Version1025Date20240308063933.php',
'OCA\\DAV\\Migration\\Version1027Date20230504122946' => __DIR__ . '/..' . '/../lib/Migration/Version1027Date20230504122946.php',
'OCA\\DAV\\Migration\\Version1029Date20221114151721' => __DIR__ . '/..' . '/../lib/Migration/Version1029Date20221114151721.php',
'OCA\\DAV\\Migration\\Version1029Date20231004091403' => __DIR__ . '/..' . '/../lib/Migration/Version1029Date20231004091403.php',

@ -170,6 +170,7 @@ OC.L10N.register(
"Delete slot" : "Ezabatu tartea",
"No working hours set" : "Ez dira laneko orduak ezarri",
"Add slot" : "Gehitu tartea",
"Weekdays" : "Astegunak",
"Monday" : "Astelehena",
"Tuesday" : "Asteartea",
"Wednesday" : "Asteazkena",

@ -168,6 +168,7 @@
"Delete slot" : "Ezabatu tartea",
"No working hours set" : "Ez dira laneko orduak ezarri",
"Add slot" : "Gehitu tartea",
"Weekdays" : "Astegunak",
"Monday" : "Astelehena",
"Tuesday" : "Asteartea",
"Wednesday" : "Asteazkena",

@ -50,9 +50,9 @@ OC.L10N.register(
"{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}",
"You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}",
"Calendar, contacts and tasks" : "Calendar, contacts and tasks",
"A <strong>calendar</strong> was modified" : "A <strong>calendar</strong> was modified",
"A calendar <strong>event</strong> was modified" : "A calendar <strong>event</strong> was modified",
"A calendar <strong>to-do</strong> was modified" : "A calendar <strong>to-do</strong> was modified",
"A <strong>calendar</strong> was modified" : "یک تقویم تغییر کرد",
"A calendar <strong>event</strong> was modified" : "یک رویداد ثبت شده در تقویم تغییر کرد",
"A calendar <strong>to-do</strong> was modified" : "یک کار ثبت شده در تقویم تغییر کرد",
"Contact birthdays" : "Contact birthdays",
"Death of %s" : "Death of %s",
"Untitled calendar" : "تقویم بدون عنوان",
@ -117,7 +117,7 @@ OC.L10N.register(
"You deleted contact {card} from address book {addressbook}" : "You deleted contact {card} from address book {addressbook}",
"{actor} updated contact {card} in address book {addressbook}" : "{actor} updated contact {card} in address book {addressbook}",
"You updated contact {card} in address book {addressbook}" : "You updated contact {card} in address book {addressbook}",
"A <strong>contact</strong> or <strong>address book</strong> was modified" : "A <strong>contact</strong> or <strong>address book</strong> was modified",
"A <strong>contact</strong> or <strong>address book</strong> was modified" : "یک مخاطب یا دفترچه آدرس تغییر کرد",
"Accounts" : "Accounts",
"System address book which holds all accounts" : "System address book which holds all accounts",
"File is not updatable: %1$s" : "File is not updatable: %1$s",
@ -189,7 +189,7 @@ OC.L10N.register(
"Are you accepting the invitation?" : "Are you accepting the invitation?",
"Tentative" : "آزمایشی",
"Your attendance was updated successfully." : "Your attendance was updated successfully.",
"To-dos" : "To-dos",
"To-dos" : "کارهای برای انجام",
"If you configure your working hours, other users will see when you are out of office when they book a meeting." : "If you configure your working hours, other users will see when you are out of office when they book a meeting."
},
"nplurals=2; plural=(n > 1);");

@ -48,9 +48,9 @@
"{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "{actor} moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}",
"You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}" : "You moved to-do {todo} from list {sourceCalendar} to list {targetCalendar}",
"Calendar, contacts and tasks" : "Calendar, contacts and tasks",
"A <strong>calendar</strong> was modified" : "A <strong>calendar</strong> was modified",
"A calendar <strong>event</strong> was modified" : "A calendar <strong>event</strong> was modified",
"A calendar <strong>to-do</strong> was modified" : "A calendar <strong>to-do</strong> was modified",
"A <strong>calendar</strong> was modified" : "یک تقویم تغییر کرد",
"A calendar <strong>event</strong> was modified" : "یک رویداد ثبت شده در تقویم تغییر کرد",
"A calendar <strong>to-do</strong> was modified" : "یک کار ثبت شده در تقویم تغییر کرد",
"Contact birthdays" : "Contact birthdays",
"Death of %s" : "Death of %s",
"Untitled calendar" : "تقویم بدون عنوان",
@ -115,7 +115,7 @@
"You deleted contact {card} from address book {addressbook}" : "You deleted contact {card} from address book {addressbook}",
"{actor} updated contact {card} in address book {addressbook}" : "{actor} updated contact {card} in address book {addressbook}",
"You updated contact {card} in address book {addressbook}" : "You updated contact {card} in address book {addressbook}",
"A <strong>contact</strong> or <strong>address book</strong> was modified" : "A <strong>contact</strong> or <strong>address book</strong> was modified",
"A <strong>contact</strong> or <strong>address book</strong> was modified" : "یک مخاطب یا دفترچه آدرس تغییر کرد",
"Accounts" : "Accounts",
"System address book which holds all accounts" : "System address book which holds all accounts",
"File is not updatable: %1$s" : "File is not updatable: %1$s",
@ -187,7 +187,7 @@
"Are you accepting the invitation?" : "Are you accepting the invitation?",
"Tentative" : "آزمایشی",
"Your attendance was updated successfully." : "Your attendance was updated successfully.",
"To-dos" : "To-dos",
"To-dos" : "کارهای برای انجام",
"If you configure your working hours, other users will see when you are out of office when they book a meeting." : "If you configure your working hours, other users will see when you are out of office when they book a meeting."
},"pluralForm" :"nplurals=2; plural=(n > 1);"
}

@ -149,9 +149,9 @@ OC.L10N.register(
"Due on %s" : "Echéance le %s",
"DAV system address book" : "Carnet d'adresses système DAV",
"No outstanding DAV system address book sync." : "Pas de synchronisation DAV en cours du carnet d'adresses du système.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "La synchronisation du carnet d'adresses système DAV n'a pas encore été effectuée car votre instance a plus de 1000 utilisateurs ou parce qu'une erreur est survenue. Merci de l'exécuter manuellement en tapant la commande \"occ dav:sync-system-addressbook\".",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "La synchronisation du carnet d'adresses système DAV n'a pas encore été effectuée car votre instance a plus de 1 000 utilisateurs ou parce qu'une erreur est survenue. Merci de l'exécuter manuellement en tapant la commande \"occ dav:sync-system-addressbook\".",
"WebDAV endpoint" : "Point d'accès WebDAV",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Votre serveur web n'est pas encore correctement configuré pour la synchronisation de fichiers parce que l'interface WebDAV semble ne pas fonctionner.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Votre serveur web nest pas encore correctement configuré pour la synchronisation de fichiers parce que linterface WebDAV semble ne pas fonctionner.",
"Migrated calendar (%1$s)" : "Agenda migré (%1$s)",
"Calendars including events, details and attendees" : "Calendriers incluant des événements, détails et participants",
"Contacts and groups" : "Contacts et groupes",

@ -147,9 +147,9 @@
"Due on %s" : "Echéance le %s",
"DAV system address book" : "Carnet d'adresses système DAV",
"No outstanding DAV system address book sync." : "Pas de synchronisation DAV en cours du carnet d'adresses du système.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "La synchronisation du carnet d'adresses système DAV n'a pas encore été effectuée car votre instance a plus de 1000 utilisateurs ou parce qu'une erreur est survenue. Merci de l'exécuter manuellement en tapant la commande \"occ dav:sync-system-addressbook\".",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "La synchronisation du carnet d'adresses système DAV n'a pas encore été effectuée car votre instance a plus de 1 000 utilisateurs ou parce qu'une erreur est survenue. Merci de l'exécuter manuellement en tapant la commande \"occ dav:sync-system-addressbook\".",
"WebDAV endpoint" : "Point d'accès WebDAV",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Votre serveur web n'est pas encore correctement configuré pour la synchronisation de fichiers parce que l'interface WebDAV semble ne pas fonctionner.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Votre serveur web nest pas encore correctement configuré pour la synchronisation de fichiers parce que linterface WebDAV semble ne pas fonctionner.",
"Migrated calendar (%1$s)" : "Agenda migré (%1$s)",
"Calendars including events, details and attendees" : "Calendriers incluant des événements, détails et participants",
"Contacts and groups" : "Contacts et groupes",

@ -151,7 +151,9 @@ OC.L10N.register(
"No outstanding DAV system address book sync." : "Não há nenhum livro de endereços DAV sendo sincronizado.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "A sincronização do catálogo de endereços do sistema DAV ainda não foi executada porque sua instância tem mais de 1000 usuários ou porque ocorreu um erro. Execute-o manualmente chamando \"occ dav:sync-system-addressbook\".",
"WebDAV endpoint" : "Endpoint WebDAV",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "Não foi possível verificar se o seu servidor web está configurado corretamente para permitir a sincronização de arquivos via WebDAV. Verifique manualmente.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Seu servidor web ainda não está configurado corretamente para permitir a sincronização de arquivos, porque a interface do WebDAV parece estar quebrada.",
"Your web server is properly set up to allow file synchronization over WebDAV." : "Seu servidor web está configurado corretamente para permitir a sincronização de arquivos via WebDAV.",
"Migrated calendar (%1$s)" : "Calendário migrado (%1$s)",
"Calendars including events, details and attendees" : "Calendários, incluindo eventos, detalhes e participantes",
"Contacts and groups" : "Contatos e grupos",

@ -149,7 +149,9 @@
"No outstanding DAV system address book sync." : "Não há nenhum livro de endereços DAV sendo sincronizado.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "A sincronização do catálogo de endereços do sistema DAV ainda não foi executada porque sua instância tem mais de 1000 usuários ou porque ocorreu um erro. Execute-o manualmente chamando \"occ dav:sync-system-addressbook\".",
"WebDAV endpoint" : "Endpoint WebDAV",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "Não foi possível verificar se o seu servidor web está configurado corretamente para permitir a sincronização de arquivos via WebDAV. Verifique manualmente.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Seu servidor web ainda não está configurado corretamente para permitir a sincronização de arquivos, porque a interface do WebDAV parece estar quebrada.",
"Your web server is properly set up to allow file synchronization over WebDAV." : "Seu servidor web está configurado corretamente para permitir a sincronização de arquivos via WebDAV.",
"Migrated calendar (%1$s)" : "Calendário migrado (%1$s)",
"Calendars including events, details and attendees" : "Calendários, incluindo eventos, detalhes e participantes",
"Contacts and groups" : "Contatos e grupos",

@ -151,7 +151,9 @@ OC.L10N.register(
"No outstanding DAV system address book sync." : "Žiadna zostávajúca synchronizácia adresára systému DAV.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "DAV synchronizácia systémového adresára ešte nebola spustená, pretože vaša inštancia má viac ako 1000 užívateľov alebo sa vyskytla chyba. Prosím, spustite ju manuálne volaním \"occ dav:sync-system-addressbook\".",
"WebDAV endpoint" : "Koncový bod WebDAV",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "Nepodarilo sa skontrolovať, či je váš webový server správne nastavený tak, aby umožňoval synchronizáciu súborov cez WebDAV. Skontrolujte prosím manuálne.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Váš webový server nie je zatiaľ správne nastavený, aby umožnil synchronizáciu súborov, pretože rozhranie WebDAV sa zdá byť nefunkčné.",
"Your web server is properly set up to allow file synchronization over WebDAV." : "Váš webový server je správne nastavený tak, aby umožňoval synchronizáciu súborov cez WebDAV.",
"Migrated calendar (%1$s)" : "Migrovaný kalendár (%1$s)",
"Calendars including events, details and attendees" : "Kalendáre vrátane udalostí, podrobností a účastníkov",
"Contacts and groups" : "Kontakty a skupiny",

@ -149,7 +149,9 @@
"No outstanding DAV system address book sync." : "Žiadna zostávajúca synchronizácia adresára systému DAV.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "DAV synchronizácia systémového adresára ešte nebola spustená, pretože vaša inštancia má viac ako 1000 užívateľov alebo sa vyskytla chyba. Prosím, spustite ju manuálne volaním \"occ dav:sync-system-addressbook\".",
"WebDAV endpoint" : "Koncový bod WebDAV",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "Nepodarilo sa skontrolovať, či je váš webový server správne nastavený tak, aby umožňoval synchronizáciu súborov cez WebDAV. Skontrolujte prosím manuálne.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Váš webový server nie je zatiaľ správne nastavený, aby umožnil synchronizáciu súborov, pretože rozhranie WebDAV sa zdá byť nefunkčné.",
"Your web server is properly set up to allow file synchronization over WebDAV." : "Váš webový server je správne nastavený tak, aby umožňoval synchronizáciu súborov cez WebDAV.",
"Migrated calendar (%1$s)" : "Migrovaný kalendár (%1$s)",
"Calendars including events, details and attendees" : "Kalendáre vrátane udalostí, podrobností a účastníkov",
"Contacts and groups" : "Kontakty a skupiny",

@ -151,7 +151,9 @@ OC.L10N.register(
"No outstanding DAV system address book sync." : "Не постоји ниједна синхронизација DAV системског адресара која треба да се обави.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "DAV синхронизација системског адресара се још увек није покренула јер ваша инстанца има више од 1000 корисника или јер је дошло до грешке. Молимо вас да га ручно покренете командом „occ dav:sync-system-addressbook”.",
"WebDAV endpoint" : "WebDAV крајња тачка",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "Није могло да се провери да ли ваш веб сервер исправно подешен тако да се омогући синхронизација фајлова преко WebDAV. Молимо вас да проверите ручно.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Сервер није правилно подешен за синхронизацију фајлова. Изгледа да је ВебДАВ сучеље покварено.",
"Your web server is properly set up to allow file synchronization over WebDAV." : "Ваш веб сервер исправно подешен тако да се омогући синхронизација фајлова преко WebDAV.",
"Migrated calendar (%1$s)" : "Мигрирани календар (%1$s)",
"Calendars including events, details and attendees" : "Календари који укључују догађаје, детаље и учеснике",
"Contacts and groups" : "Контакти и групе",

@ -149,7 +149,9 @@
"No outstanding DAV system address book sync." : "Не постоји ниједна синхронизација DAV системског адресара која треба да се обави.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "DAV синхронизација системског адресара се још увек није покренула јер ваша инстанца има више од 1000 корисника или јер је дошло до грешке. Молимо вас да га ручно покренете командом „occ dav:sync-system-addressbook”.",
"WebDAV endpoint" : "WebDAV крајња тачка",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "Није могло да се провери да ли ваш веб сервер исправно подешен тако да се омогући синхронизација фајлова преко WebDAV. Молимо вас да проверите ручно.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Сервер није правилно подешен за синхронизацију фајлова. Изгледа да је ВебДАВ сучеље покварено.",
"Your web server is properly set up to allow file synchronization over WebDAV." : "Ваш веб сервер исправно подешен тако да се омогући синхронизација фајлова преко WebDAV.",
"Migrated calendar (%1$s)" : "Мигрирани календар (%1$s)",
"Calendars including events, details and attendees" : "Календари који укључују догађаје, детаље и учеснике",
"Contacts and groups" : "Контакти и групе",

@ -151,7 +151,9 @@ OC.L10N.register(
"No outstanding DAV system address book sync." : "Ingen utestående synkronisering för DAV-systemets adressbok.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "DAV-systemets adressbokssynkronisering har inte körts ännu eftersom din instans har fler än 1000 användare eller för att ett fel uppstod. Kör det manuellt genom att anropa \"occ dav:sync-system-addressbook\".",
"WebDAV endpoint" : "WebDAV endpoint",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "Det gick inte att kontrollera att din webbserver är korrekt inställd för att tillåta filsynkronisering över WebDAV. Kontrollera manuellt.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Din webbserver är ännu inte korrekt inställd för att tillåta filsynkronisering, eftersom WebDAV-gränssnittet verkar vara trasigt.",
"Your web server is properly set up to allow file synchronization over WebDAV." : "Din webbserver är korrekt inställd för att tillåta filsynkronisering över WebDAV.",
"Migrated calendar (%1$s)" : "Migrerade kalender (%1$s)",
"Calendars including events, details and attendees" : "Kalendrar inklusive händelser, detaljer och deltagare",
"Contacts and groups" : "Kontakter och grupper",

@ -149,7 +149,9 @@
"No outstanding DAV system address book sync." : "Ingen utestående synkronisering för DAV-systemets adressbok.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "DAV-systemets adressbokssynkronisering har inte körts ännu eftersom din instans har fler än 1000 användare eller för att ett fel uppstod. Kör det manuellt genom att anropa \"occ dav:sync-system-addressbook\".",
"WebDAV endpoint" : "WebDAV endpoint",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "Det gick inte att kontrollera att din webbserver är korrekt inställd för att tillåta filsynkronisering över WebDAV. Kontrollera manuellt.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Din webbserver är ännu inte korrekt inställd för att tillåta filsynkronisering, eftersom WebDAV-gränssnittet verkar vara trasigt.",
"Your web server is properly set up to allow file synchronization over WebDAV." : "Din webbserver är korrekt inställd för att tillåta filsynkronisering över WebDAV.",
"Migrated calendar (%1$s)" : "Migrerade kalender (%1$s)",
"Calendars including events, details and attendees" : "Kalendrar inklusive händelser, detaljer och deltagare",
"Contacts and groups" : "Kontakter och grupper",

@ -75,14 +75,14 @@ OC.L10N.register(
"Cancelled: %1$s" : "İptal edildi: %1$s",
"\"%1$s\" has been canceled" : "\"%1$s\" iptal edildi",
"Re: %1$s" : "Ynt: %1$s",
"%1$s has accepted your invitation" : "%1$s çağrınızı kabul etti",
"%1$s has tentatively accepted your invitation" : "%1$s çağrınızı belirsiz olarak kabul etti",
"%1$s has declined your invitation" : "%1$s çağrınızı reddetti.",
"%1$s has responded to your invitation" : "%1$s çağrınızı yanıtladı",
"Invitation updated: %1$s" : "Çağrı güncellendi: %1$s",
"%1$s has accepted your invitation" : "%1$s davetinizi kabul etti",
"%1$s has tentatively accepted your invitation" : "%1$s davetinizi belirsiz olarak kabul etti",
"%1$s has declined your invitation" : "%1$s davetinizi reddetti.",
"%1$s has responded to your invitation" : "%1$s davetinizi yanıtladı",
"Invitation updated: %1$s" : "Davet güncellendi: %1$s",
"%1$s updated the event \"%2$s\"" : "%1$s, \"%2$s\" etkinliğini güncelledi",
"Invitation: %1$s" : "Çağrı: %1$s",
"%1$s would like to invite you to \"%2$s\"" : "%1$s, size \"%2$s\" için çağrı gönderdi",
"Invitation: %1$s" : "Davet: %1$s",
"%1$s would like to invite you to \"%2$s\"" : "%1$s, size \"%2$s\" için davet gönderdi",
"Organizer:" : "Düzenleyen:",
"Attendees:" : "Katılımcılar:",
"Title:" : "Başlık:",
@ -151,7 +151,9 @@ OC.L10N.register(
"No outstanding DAV system address book sync." : "Bekleyen bir DAV sistemi adres defteri eşitlemesi yok.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "Kopyanızda 1000 üzerinde kullanıcı olduğundan ya da bir sorun çıktığından DAV sistemi adres defteri eşitlemesi henüz yapılmamış. Lütfen \"occ dav:sync-system-addressbook\" komutunu yürüterek el ile eşitleyin.",
"WebDAV endpoint" : "WebDAV bağlantı noktası",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "Site sunucunuzun WebDAV üzerinden dosya eşitlemesi için doğru şekilde ayarlanıp ayarlanmadığı denetlenemedi. Lütfen el ile denetleyin.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Site sunucunuz dosya eşitlemesi için doğru şekilde ayarlanmamış. WebDAV arabirimi sorunlu görünüyor.",
"Your web server is properly set up to allow file synchronization over WebDAV." : "Site sunucunuz WebDAV üzerinden dosya eşitlemesi için doğru şekilde ayarlanmış.",
"Migrated calendar (%1$s)" : "Aktarılmış takvim (%1$s)",
"Calendars including events, details and attendees" : "Etkinlikler, bilgiler ve katılımcılar ile takvimler",
"Contacts and groups" : "Kişiler ve gruplar",
@ -171,6 +173,7 @@ OC.L10N.register(
"Delete slot" : "Aralığı sil",
"No working hours set" : "Çalışma saatleri ayarlanmamış",
"Add slot" : "Aralık ekle",
"Weekdays" : "Hafta içi günleri",
"Monday" : "Pazartesi",
"Tuesday" : "Salı",
"Wednesday" : "Çarşamba",
@ -189,7 +192,7 @@ OC.L10N.register(
"Absence" : "İşe gelmeme",
"Configure your next absence period." : "Sonraki işe gelmeme aralığınızı yapılandırın.",
"Calendar server" : "Takvim sunucusu",
"Send invitations to attendees" : "Katılımcılara çağrılar gönderilsin",
"Send invitations to attendees" : "Katılımcılara davet gönder",
"Automatically generate a birthday calendar" : "Doğum günü takvimi otomatik oluşturulsun",
"Birthday calendars will be generated by a background job." : "Bu seçenek etkinleştirildiğinde, doğum günü takvimi arka plan görevi olarak oluşturulur.",
"Hence they will not be available immediately after enabling but will show up after some time." : "Etkinleştirildikten hemen sonra görüntülenmez, bir süre sonra görüntülenir.",
@ -202,7 +205,7 @@ OC.L10N.register(
"Please make sure to properly set up {emailopen}the email server{linkclose}." : "Lütfen {emailopen}e-posta sunucusunu{linkclose} doğru ayarladığınızdan emin olun.",
"There was an error updating your attendance status." : "Katılım durumunuz güncellenirken bir sorun çıktı.",
"Please contact the organizer directly." : "Lütfen düzenleyici ile doğrudan görüşün.",
"Are you accepting the invitation?" : "Çağrıyı kabul ediyor musunuz?",
"Are you accepting the invitation?" : "Daveti kabul ediyor musunuz?",
"Tentative" : "Kesin değil",
"Your attendance was updated successfully." : "Katılımınız güncellendi.",
"To-dos" : "Yapılacak işler",

@ -73,14 +73,14 @@
"Cancelled: %1$s" : "İptal edildi: %1$s",
"\"%1$s\" has been canceled" : "\"%1$s\" iptal edildi",
"Re: %1$s" : "Ynt: %1$s",
"%1$s has accepted your invitation" : "%1$s çağrınızı kabul etti",
"%1$s has tentatively accepted your invitation" : "%1$s çağrınızı belirsiz olarak kabul etti",
"%1$s has declined your invitation" : "%1$s çağrınızı reddetti.",
"%1$s has responded to your invitation" : "%1$s çağrınızı yanıtladı",
"Invitation updated: %1$s" : "Çağrı güncellendi: %1$s",
"%1$s has accepted your invitation" : "%1$s davetinizi kabul etti",
"%1$s has tentatively accepted your invitation" : "%1$s davetinizi belirsiz olarak kabul etti",
"%1$s has declined your invitation" : "%1$s davetinizi reddetti.",
"%1$s has responded to your invitation" : "%1$s davetinizi yanıtladı",
"Invitation updated: %1$s" : "Davet güncellendi: %1$s",
"%1$s updated the event \"%2$s\"" : "%1$s, \"%2$s\" etkinliğini güncelledi",
"Invitation: %1$s" : "Çağrı: %1$s",
"%1$s would like to invite you to \"%2$s\"" : "%1$s, size \"%2$s\" için çağrı gönderdi",
"Invitation: %1$s" : "Davet: %1$s",
"%1$s would like to invite you to \"%2$s\"" : "%1$s, size \"%2$s\" için davet gönderdi",
"Organizer:" : "Düzenleyen:",
"Attendees:" : "Katılımcılar:",
"Title:" : "Başlık:",
@ -149,7 +149,9 @@
"No outstanding DAV system address book sync." : "Bekleyen bir DAV sistemi adres defteri eşitlemesi yok.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "Kopyanızda 1000 üzerinde kullanıcı olduğundan ya da bir sorun çıktığından DAV sistemi adres defteri eşitlemesi henüz yapılmamış. Lütfen \"occ dav:sync-system-addressbook\" komutunu yürüterek el ile eşitleyin.",
"WebDAV endpoint" : "WebDAV bağlantı noktası",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "Site sunucunuzun WebDAV üzerinden dosya eşitlemesi için doğru şekilde ayarlanıp ayarlanmadığı denetlenemedi. Lütfen el ile denetleyin.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Site sunucunuz dosya eşitlemesi için doğru şekilde ayarlanmamış. WebDAV arabirimi sorunlu görünüyor.",
"Your web server is properly set up to allow file synchronization over WebDAV." : "Site sunucunuz WebDAV üzerinden dosya eşitlemesi için doğru şekilde ayarlanmış.",
"Migrated calendar (%1$s)" : "Aktarılmış takvim (%1$s)",
"Calendars including events, details and attendees" : "Etkinlikler, bilgiler ve katılımcılar ile takvimler",
"Contacts and groups" : "Kişiler ve gruplar",
@ -169,6 +171,7 @@
"Delete slot" : "Aralığı sil",
"No working hours set" : "Çalışma saatleri ayarlanmamış",
"Add slot" : "Aralık ekle",
"Weekdays" : "Hafta içi günleri",
"Monday" : "Pazartesi",
"Tuesday" : "Salı",
"Wednesday" : "Çarşamba",
@ -187,7 +190,7 @@
"Absence" : "İşe gelmeme",
"Configure your next absence period." : "Sonraki işe gelmeme aralığınızı yapılandırın.",
"Calendar server" : "Takvim sunucusu",
"Send invitations to attendees" : "Katılımcılara çağrılar gönderilsin",
"Send invitations to attendees" : "Katılımcılara davet gönder",
"Automatically generate a birthday calendar" : "Doğum günü takvimi otomatik oluşturulsun",
"Birthday calendars will be generated by a background job." : "Bu seçenek etkinleştirildiğinde, doğum günü takvimi arka plan görevi olarak oluşturulur.",
"Hence they will not be available immediately after enabling but will show up after some time." : "Etkinleştirildikten hemen sonra görüntülenmez, bir süre sonra görüntülenir.",
@ -200,7 +203,7 @@
"Please make sure to properly set up {emailopen}the email server{linkclose}." : "Lütfen {emailopen}e-posta sunucusunu{linkclose} doğru ayarladığınızdan emin olun.",
"There was an error updating your attendance status." : "Katılım durumunuz güncellenirken bir sorun çıktı.",
"Please contact the organizer directly." : "Lütfen düzenleyici ile doğrudan görüşün.",
"Are you accepting the invitation?" : "Çağrıyı kabul ediyor musunuz?",
"Are you accepting the invitation?" : "Daveti kabul ediyor musunuz?",
"Tentative" : "Kesin değil",
"Your attendance was updated successfully." : "Katılımınız güncellendi.",
"To-dos" : "Yapılacak işler",

@ -151,7 +151,9 @@ OC.L10N.register(
"No outstanding DAV system address book sync." : "沒有未完成的 DAV 系統通訊錄同步。",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "DAV 系統通訊錄同步尚未執行,因為您的站台有超過 1000 個使用者或是因為遇到錯誤。請透過呼叫「occ dav:sync-system-addressbook」手動執行。",
"WebDAV endpoint" : "WebDAV 端點",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "無法檢查您的網路伺服器是否已正確設定以允許透過 WebDAV 進行檔案同步。請手動檢查。",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "WebDAV 介面似乎為故障狀態,導致您的網頁伺服器無法提供檔案同步功能。",
"Your web server is properly set up to allow file synchronization over WebDAV." : "您的網路伺服器已正確設定為允許透過 WebDAV 進行檔案同步。",
"Migrated calendar (%1$s)" : "已導入的行事曆 (%1$s)",
"Calendars including events, details and attendees" : "行事曆,包含事件、詳細資訊及參與者",
"Contacts and groups" : "聯絡人與群組",

@ -149,7 +149,9 @@
"No outstanding DAV system address book sync." : "沒有未完成的 DAV 系統通訊錄同步。",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "DAV 系統通訊錄同步尚未執行,因為您的站台有超過 1000 個使用者或是因為遇到錯誤。請透過呼叫「occ dav:sync-system-addressbook」手動執行。",
"WebDAV endpoint" : "WebDAV 端點",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "無法檢查您的網路伺服器是否已正確設定以允許透過 WebDAV 進行檔案同步。請手動檢查。",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "WebDAV 介面似乎為故障狀態,導致您的網頁伺服器無法提供檔案同步功能。",
"Your web server is properly set up to allow file synchronization over WebDAV." : "您的網路伺服器已正確設定為允許透過 WebDAV 進行檔案同步。",
"Migrated calendar (%1$s)" : "已導入的行事曆 (%1$s)",
"Calendars including events, details and attendees" : "行事曆,包含事件、詳細資訊及參與者",
"Contacts and groups" : "聯絡人與群組",

@ -52,9 +52,10 @@ class PruneOutdatedSyncTokensJob extends TimedJob {
public function run($argument) {
$limit = max(1, (int) $this->config->getAppValue(Application::APP_ID, 'totalNumberOfSyncTokensToKeep', '10000'));
$retention = max(7, (int) $this->config->getAppValue(Application::APP_ID, 'syncTokensRetentionDays', '60')) * 24 * 3600;
$prunedCalendarSyncTokens = $this->calDavBackend->pruneOutdatedSyncTokens($limit);
$prunedAddressBookSyncTokens = $this->cardDavBackend->pruneOutdatedSyncTokens($limit);
$prunedCalendarSyncTokens = $this->calDavBackend->pruneOutdatedSyncTokens($limit, $retention);
$prunedAddressBookSyncTokens = $this->cardDavBackend->pruneOutdatedSyncTokens($limit, $retention);
$this->logger->info('Pruned {calendarSyncTokensNumber} calendar sync tokens and {addressBooksSyncTokensNumber} address book sync tokens', [
'calendarSyncTokensNumber' => $prunedCalendarSyncTokens,

@ -2785,6 +2785,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
'calendarid' => $query->createNamedParameter($calendarId),
'operation' => $query->createNamedParameter($operation),
'calendartype' => $query->createNamedParameter($calendarType),
'created_at' => time(),
]);
foreach ($objectUris as $uri) {
$query->setParameter('uri', $uri);
@ -3259,7 +3260,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
/**
* @throws \InvalidArgumentException
*/
public function pruneOutdatedSyncTokens(int $keep = 10_000): int {
public function pruneOutdatedSyncTokens(int $keep, int $retention): int {
if ($keep < 0) {
throw new \InvalidArgumentException();
}
@ -3277,7 +3278,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
$query = $this->db->getQueryBuilder();
$query->delete('calendarchanges')
->where($query->expr()->lte('id', $query->createNamedParameter($maxId - $keep, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT));
->where(
$query->expr()->lte('id', $query->createNamedParameter($maxId - $keep, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT),
$query->expr()->lte('created_at', $query->createNamedParameter($retention)),
);
return $query->executeStatement();
}

@ -116,9 +116,13 @@ class CalendarObject extends \Sabre\CalDAV\CalendarObject {
case 'CREATED':
case 'DTSTART':
case 'RRULE':
case 'RECURRENCE-ID':
case 'RDATE':
case 'DURATION':
case 'DTEND':
case 'CLASS':
case 'EXRULE':
case 'EXDATE':
case 'UID':
break;
case 'SUMMARY':

@ -65,7 +65,7 @@ class EventComparisonService {
$eventToFilterData[] = IMipService::readPropertyWithDefault($eventToFilter, $eventDiff, '');
}
// events are identical and can be removed
if (empty(array_diff($filterEventData, $eventToFilterData))) {
if ($filterEventData === $eventToFilterData) {
unset($eventsToFilter[$k]);
return true;
}

@ -118,7 +118,7 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable, IMov
],
[
'privilege' => '{DAV:}write-properties',
'principal' => '{DAV:}authenticated',
'principal' => $this->getOwner(),
'protected' => true,
],
];
@ -129,6 +129,11 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable, IMov
'principal' => '{DAV:}authenticated',
'protected' => true,
];
$acl[] = [
'privilege' => '{DAV:}write-properties',
'principal' => '{DAV:}authenticated',
'protected' => true,
];
}
if (!$this->isShared()) {

@ -983,6 +983,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
'synctoken' => $query->createNamedParameter($syncToken),
'addressbookid' => $query->createNamedParameter($addressBookId),
'operation' => $query->createNamedParameter($operation),
'created_at' => time(),
])
->executeStatement();
@ -1415,7 +1416,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
/**
* @throws \InvalidArgumentException
*/
public function pruneOutdatedSyncTokens(int $keep = 10_000): int {
public function pruneOutdatedSyncTokens(int $keep, int $retention): int {
if ($keep < 0) {
throw new \InvalidArgumentException();
}
@ -1433,7 +1434,10 @@ class CardDavBackend implements BackendInterface, SyncSupport {
$query = $this->db->getQueryBuilder();
$query->delete('addressbookchanges')
->where($query->expr()->lte('id', $query->createNamedParameter($maxId - $keep, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT));
->where(
$query->expr()->lte('id', $query->createNamedParameter($maxId - $keep, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT),
$query->expr()->lte('created_at', $query->createNamedParameter($retention)),
);
return $query->executeStatement();
}

@ -214,7 +214,7 @@ abstract class Backend {
'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
'protected' => true,
];
} elseif ($this->service->getResourceType() === 'calendar') {
} elseif (in_array($this->service->getResourceType(), ['calendar','addressbook'])) {
// Allow changing the properties of read only calendars,
// so users can change the visibility.
$acl[] = [

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2024 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @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 OCA\DAV\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\Types;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
class Version1025Date20240308063933 extends SimpleMigrationStep {
private IDBConnection $db;
public function __construct(IDBConnection $db) {
$this->db = $db;
}
/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
foreach (['addressbookchanges', 'calendarchanges'] as $tableName) {
$table = $schema->getTable($tableName);
if (!$table->hasColumn('created_at')) {
$table->addColumn('created_at', Types::INTEGER, [
'notnull' => true,
'length' => 4,
'default' => 0,
]);
}
}
return $schema;
}
public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options): void {
foreach (['addressbookchanges', 'calendarchanges'] as $tableName) {
$qb = $this->db->getQueryBuilder();
$update = $qb->update($tableName)
->set('created_at', $qb->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
->where(
$qb->expr()->eq('created_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)),
);
$updated = $update->executeStatement();
$output->debug('Added a default creation timestamp to ' . $updated . ' rows in ' . $tableName);
}
}
}

@ -37,62 +37,15 @@ use Sabre\DAV\Exception\NotFound;
* Mapping node for system tag to object id
*/
class SystemTagMappingNode implements \Sabre\DAV\INode {
/**
* @var ISystemTag
*/
protected $tag;
/**
* @var string
*/
private $objectId;
/**
* @var string
*/
private $objectType;
/**
* User
*
* @var IUser
*/
protected $user;
/**
* @var ISystemTagManager
*/
protected $tagManager;
/**
* @var ISystemTagObjectMapper
*/
private $tagMapper;
/**
* Sets up the node, expects a full path name
*
* @param ISystemTag $tag system tag
* @param string $objectId
* @param string $objectType
* @param IUser $user user
* @param ISystemTagManager $tagManager
* @param ISystemTagObjectMapper $tagMapper
*/
public function __construct(
ISystemTag $tag,
$objectId,
$objectType,
IUser $user,
ISystemTagManager $tagManager,
ISystemTagObjectMapper $tagMapper
private ISystemTag $tag,
private string $objectId,
private string $objectType,
private IUser $user,
private ISystemTagManager $tagManager,
private ISystemTagObjectMapper $tagMapper,
private \Closure $childWriteAccessFunction,
) {
$this->tag = $tag;
$this->objectId = $objectId;
$this->objectType = $objectType;
$this->user = $user;
$this->tagManager = $tagManager;
$this->tagMapper = $tagMapper;
}
/**
@ -166,6 +119,10 @@ class SystemTagMappingNode implements \Sabre\DAV\INode {
if (!$this->tagManager->canUserAssignTag($this->tag, $this->user)) {
throw new Forbidden('No permission to unassign tag ' . $this->tag->getId());
}
$writeAccessFunction = $this->childWriteAccessFunction;
if (!$writeAccessFunction($this->objectId)) {
throw new Forbidden('No permission to unassign tag to ' . $this->objectId);
}
$this->tagMapper->unassignTags($this->objectId, $this->objectType, $this->tag->getId());
} catch (TagNotFoundException $e) {
// can happen if concurrent deletion occurred

@ -40,56 +40,14 @@ use Sabre\DAV\ICollection;
* Collection containing tags by object id
*/
class SystemTagsObjectMappingCollection implements ICollection {
/**
* @var string
*/
private $objectId;
/**
* @var string
*/
private $objectType;
/**
* @var ISystemTagManager
*/
private $tagManager;
/**
* @var ISystemTagObjectMapper
*/
private $tagMapper;
/**
* User
*
* @var IUser
*/
private $user;
/**
* Constructor
*
* @param string $objectId object id
* @param string $objectType object type
* @param IUser $user user
* @param ISystemTagManager $tagManager tag manager
* @param ISystemTagObjectMapper $tagMapper tag mapper
*/
public function __construct(
$objectId,
$objectType,
IUser $user,
ISystemTagManager $tagManager,
ISystemTagObjectMapper $tagMapper
private string $objectId,
private string $objectType,
private IUser $user,
private ISystemTagManager $tagManager,
private ISystemTagObjectMapper $tagMapper,
protected \Closure $childWriteAccessFunction,
) {
$this->tagManager = $tagManager;
$this->tagMapper = $tagMapper;
$this->objectId = $objectId;
$this->objectType = $objectType;
$this->user = $user;
}
/**
@ -106,7 +64,10 @@ class SystemTagsObjectMappingCollection implements ICollection {
if (!$this->tagManager->canUserAssignTag($tag, $this->user)) {
throw new Forbidden('No permission to assign tag ' . $tagId);
}
$writeAccessFunction = $this->childWriteAccessFunction;
if (!$writeAccessFunction($this->objectId)) {
throw new Forbidden('No permission to assign tag to ' . $this->objectId);
}
$this->tagMapper->assignTags($this->objectId, $this->objectType, $tagId);
} catch (TagNotFoundException $e) {
throw new PreconditionFailed('Tag with id ' . $tagId . ' does not exist, cannot assign');
@ -224,7 +185,8 @@ class SystemTagsObjectMappingCollection implements ICollection {
$this->objectType,
$this->user,
$this->tagManager,
$this->tagMapper
$this->tagMapper,
$this->childWriteAccessFunction,
);
}
}

@ -38,61 +38,15 @@ use Sabre\DAV\ICollection;
* Collection containing object ids by object type
*/
class SystemTagsObjectTypeCollection implements ICollection {
/**
* @var string
*/
private $objectType;
/**
* @var ISystemTagManager
*/
private $tagManager;
/**
* @var ISystemTagObjectMapper
*/
private $tagMapper;
/**
* @var IGroupManager
*/
private $groupManager;
/**
* @var IUserSession
*/
private $userSession;
/**
* @var \Closure
**/
protected $childExistsFunction;
/**
* Constructor
*
* @param string $objectType object type
* @param ISystemTagManager $tagManager
* @param ISystemTagObjectMapper $tagMapper
* @param IUserSession $userSession
* @param IGroupManager $groupManager
* @param \Closure $childExistsFunction
*/
public function __construct(
$objectType,
ISystemTagManager $tagManager,
ISystemTagObjectMapper $tagMapper,
IUserSession $userSession,
IGroupManager $groupManager,
\Closure $childExistsFunction
private string $objectType,
private ISystemTagManager $tagManager,
private ISystemTagObjectMapper $tagMapper,
private IUserSession $userSession,
private IGroupManager $groupManager,
protected \Closure $childExistsFunction,
protected \Closure $childWriteAccessFunction,
) {
$this->tagManager = $tagManager;
$this->tagMapper = $tagMapper;
$this->objectType = $objectType;
$this->userSession = $userSession;
$this->groupManager = $groupManager;
$this->childExistsFunction = $childExistsFunction;
}
/**
@ -134,7 +88,8 @@ class SystemTagsObjectTypeCollection implements ICollection {
$this->objectType,
$this->userSession->getUser(),
$this->tagManager,
$this->tagMapper
$this->tagMapper,
$this->childWriteAccessFunction,
);
}

@ -26,6 +26,7 @@
*/
namespace OCA\DAV\SystemTag;
use OCP\Constants;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\IRootFolder;
use OCP\IGroupManager;
@ -60,7 +61,21 @@ class SystemTagsRelationsCollection extends SimpleCollection {
} else {
return false;
}
}
},
function (string $name) use ($rootFolder, $userSession): bool {
$user = $userSession->getUser();
if ($user) {
$nodes = $rootFolder->getUserFolder($user->getUID())->getById((int)$name);
foreach ($nodes as $node) {
if (($node->getPermissions() & Constants::PERMISSION_UPDATE) === Constants::PERMISSION_UPDATE) {
return true;
}
}
return false;
} else {
return false;
}
},
),
];
@ -75,7 +90,8 @@ class SystemTagsRelationsCollection extends SimpleCollection {
$tagMapper,
$userSession,
$groupManager,
$entityExistsFunction
$entityExistsFunction,
fn ($name) => true,
);
}

@ -58,7 +58,7 @@ export async function findScheduleInboxAvailability() {
</x0:propfind>`,
})
const xml = await parseXML(response.data)
const xml = await parseXML(await response.text())
if (!xml) {
return undefined

@ -29,6 +29,7 @@ declare(strict_types=1);
*/
namespace OCA\DAV\Tests\unit\BackgroundJob;
use InvalidArgumentException;
use OCA\DAV\AppInfo\Application;
use OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob;
use OCA\DAV\CalDAV\CalDavBackend;
@ -72,18 +73,27 @@ class PruneOutdatedSyncTokensJobTest extends TestCase {
/**
* @dataProvider dataForTestRun
*/
public function testRun(string $configValue, int $actualLimit, int $deletedCalendarSyncTokens, int $deletedAddressBookSyncTokens): void {
$this->config->expects($this->once())
public function testRun(string $configToKeep, string $configRetentionDays, int $actualLimit, int $retentionDays, int $deletedCalendarSyncTokens, int $deletedAddressBookSyncTokens): void {
$this->config->expects($this->exactly(2))
->method('getAppValue')
->with(Application::APP_ID, 'totalNumberOfSyncTokensToKeep', '10000')
->willReturn($configValue);
->with(Application::APP_ID, self::anything(), self::anything())
->willReturnCallback(function ($app, $key) use ($configToKeep, $configRetentionDays) {
switch ($key) {
case 'totalNumberOfSyncTokensToKeep':
return $configToKeep;
case 'syncTokensRetentionDays':
return $configRetentionDays;
default:
throw new InvalidArgumentException();
}
});
$this->calDavBackend->expects($this->once())
->method('pruneOutdatedSyncTokens')
->with($actualLimit)
->willReturn($deletedCalendarSyncTokens);
$this->cardDavBackend->expects($this->once())
->method('pruneOutdatedSyncTokens')
->with($actualLimit)
->with($actualLimit, $retentionDays)
->willReturn($deletedAddressBookSyncTokens);
$this->logger->expects($this->once())
->method('info')
@ -97,8 +107,9 @@ class PruneOutdatedSyncTokensJobTest extends TestCase {
public function dataForTestRun(): array {
return [
['100', 100, 2, 3],
['0', 1, 0, 0]
['100', '2', 100, 7 * 24 * 3600, 2, 3],
['100', '14', 100, 14 * 24 * 3600, 2, 3],
['0', '60', 1, 60 * 24 * 3600, 0, 0]
];
}
}

@ -46,6 +46,7 @@ use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\PropPatch;
use Sabre\DAV\Xml\Property\Href;
use Sabre\DAVACL\IACL;
use function time;
/**
* Class CalDavBackendTest
@ -1357,7 +1358,12 @@ END:VEVENT
END:VCALENDAR
EOD;
$this->backend->updateCalendarObject($calendarId, $uri, $calData);
$deleted = $this->backend->pruneOutdatedSyncTokens(0);
// Keep everything
$deleted = $this->backend->pruneOutdatedSyncTokens(0, 0);
self::assertSame(0, $deleted);
$deleted = $this->backend->pruneOutdatedSyncTokens(0, time());
// At least one from the object creation and one from the object update
$this->assertGreaterThanOrEqual(2, $deleted);
$changes = $this->backend->getChangesForCalendar($calendarId, $syncToken, 1);
@ -1423,7 +1429,7 @@ EOD;
$this->assertEmpty($changes['deleted']);
// Delete all but last change
$deleted = $this->backend->pruneOutdatedSyncTokens(1);
$deleted = $this->backend->pruneOutdatedSyncTokens(1, time());
$this->assertEquals(1, $deleted); // We had two changes before, now one
// Only update should remain
@ -1433,7 +1439,8 @@ EOD;
$this->assertEmpty($changes['deleted']);
// Check that no crash occurs when prune is called without current changes
$deleted = $this->backend->pruneOutdatedSyncTokens(1);
$deleted = $this->backend->pruneOutdatedSyncTokens(1, time());
self::assertSame(0, $deleted);
}
public function testSearchAndExpandRecurrences() {

@ -4,6 +4,7 @@ declare(strict_types=1);
/**
* @copyright 2023 Daniel Kesselberg <mail@danielkesselberg.de>
* @copyright 2024 Robert C. Schaller <gtbc_robert.schaller@rsxc.de>
*
* @author 2023 Daniel Kesselberg <mail@danielkesselberg.de>
*
@ -137,4 +138,70 @@ class EventComparisonServiceTest extends TestCase {
$this->assertEquals([$vEventOld2], $result['old']);
$this->assertEquals([$vEventNew2], $result['new']);
}
// First test to certify fix for issue nextcloud/server#41084
public function testSequenceNumberIncrementDetectedForFirstModificationToEventWithoutZeroInit(): void {
$vCalendarOld = new VCalendar();
$vCalendarNew = new VCalendar();
$vEventOld = $vCalendarOld->add('VEVENT', [
'UID' => 'uid-1234',
'LAST-MODIFIED' => 123456,
// 'SEQUENCE' => 0, // sequence number may not be set to zero during event creation and instead fully omitted
'SUMMARY' => 'Fellowship meeting',
'DTSTART' => new \DateTime('2016-01-01 00:00:00'),
'RRULE' => 'FREQ=DAILY;INTERVAL=1;UNTIL=20160201T000000Z',
]);
$vEventOld->add('ORGANIZER', 'mailto:gandalf@wiz.ard');
$vEventOld->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']);
$vEventNew = $vCalendarNew->add('VEVENT', [
'UID' => 'uid-1234',
'LAST-MODIFIED' => 123456,
'SEQUENCE' => 1,
'SUMMARY' => 'Fellowship meeting',
'DTSTART' => new \DateTime('2016-01-01 00:00:00'),
'RRULE' => 'FREQ=DAILY;INTERVAL=1;UNTIL=20160201T000000Z',
]);
$vEventNew->add('ORGANIZER', 'mailto:gandalf@wiz.ard');
$vEventNew->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']);
$result = $this->eventComparisonService->findModified($vCalendarNew, $vCalendarOld);
$this->assertEquals([$vEventOld], $result['old']);
$this->assertEquals([$vEventNew], $result['new']);
}
// Second test to certify fix for issue nextcloud/server#41084
public function testSequenceNumberIncrementDetectedForFirstModificationToEventWithZeroInit(): void {
$vCalendarOld = new VCalendar();
$vCalendarNew = new VCalendar();
$vEventOld = $vCalendarOld->add('VEVENT', [
'UID' => 'uid-1234',
'LAST-MODIFIED' => 123456,
'SEQUENCE' => 0,
'SUMMARY' => 'Fellowship meeting',
'DTSTART' => new \DateTime('2016-01-01 00:00:00'),
'RRULE' => 'FREQ=DAILY;INTERVAL=1;UNTIL=20160201T000000Z',
]);
$vEventOld->add('ORGANIZER', 'mailto:gandalf@wiz.ard');
$vEventOld->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']);
$vEventNew = $vCalendarNew->add('VEVENT', [
'UID' => 'uid-1234',
'LAST-MODIFIED' => 123456,
'SEQUENCE' => 1,
'SUMMARY' => 'Fellowship meeting',
'DTSTART' => new \DateTime('2016-01-01 00:00:00'),
'RRULE' => 'FREQ=DAILY;INTERVAL=1;UNTIL=20160201T000000Z',
]);
$vEventNew->add('ORGANIZER', 'mailto:gandalf@wiz.ard');
$vEventNew->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']);
$result = $this->eventComparisonService->findModified($vCalendarNew, $vCalendarOld);
$this->assertEquals([$vEventOld], $result['old']);
$this->assertEquals([$vEventNew], $result['new']);
}
}

@ -169,7 +169,7 @@ class AddressBookTest extends TestCase {
'protected' => true
], [
'privilege' => '{DAV:}write-properties',
'principal' => '{DAV:}authenticated',
'principal' => $hasOwnerSet ? 'user1' : 'user2',
'protected' => true
]];
if ($hasOwnerSet) {

@ -60,6 +60,7 @@ use Sabre\DAV\PropPatch;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Property\Text;
use Test\TestCase;
use function time;
/**
* Class CardDavBackendTest
@ -880,7 +881,12 @@ class CardDavBackendTest extends TestCase {
$uri = $this->getUniqueID('card');
$this->backend->createCard($addressBookId, $uri, $this->vcardTest0);
$this->backend->updateCard($addressBookId, $uri, $this->vcardTest1);
$deleted = $this->backend->pruneOutdatedSyncTokens(0);
// Do not delete anything if week data as old as ts=0
$deleted = $this->backend->pruneOutdatedSyncTokens(0, 0);
self::assertSame(0, $deleted);
$deleted = $this->backend->pruneOutdatedSyncTokens(0, time());
// At least one from the object creation and one from the object update
$this->assertGreaterThanOrEqual(2, $deleted);
$changes = $this->backend->getChangesForAddressBook($addressBookId, $syncToken, 1);
@ -912,7 +918,7 @@ class CardDavBackendTest extends TestCase {
$this->assertEmpty($changes['deleted']);
// Delete all but last change
$deleted = $this->backend->pruneOutdatedSyncTokens(1);
$deleted = $this->backend->pruneOutdatedSyncTokens(1, time());
$this->assertEquals(1, $deleted); // We had two changes before, now one
// Only update should remain
@ -920,8 +926,8 @@ class CardDavBackendTest extends TestCase {
$this->assertEmpty($changes['added']);
$this->assertEquals(1, count($changes['modified']));
$this->assertEmpty($changes['deleted']);
// Check that no crash occurs when prune is called without current changes
$deleted = $this->backend->pruneOutdatedSyncTokens(1);
$deleted = $this->backend->pruneOutdatedSyncTokens(1, time());
}
}

@ -33,21 +33,9 @@ use OCP\SystemTag\ISystemTagObjectMapper;
use OCP\SystemTag\TagNotFoundException;
class SystemTagMappingNodeTest extends \Test\TestCase {
/**
* @var \OCP\SystemTag\ISystemTagManager
*/
private $tagManager;
/**
* @var \OCP\SystemTag\ISystemTagObjectMapper
*/
private $tagMapper;
/**
* @var \OCP\IUser
*/
private $user;
private ISystemTagManager $tagManager;
private ISystemTagObjectMapper $tagMapper;
private IUser $user;
protected function setUp(): void {
parent::setUp();
@ -60,7 +48,7 @@ class SystemTagMappingNodeTest extends \Test\TestCase {
->getMock();
}
public function getMappingNode($tag = null) {
public function getMappingNode($tag = null, array $writableNodeIds = []) {
if ($tag === null) {
$tag = new SystemTag(1, 'Test', true, true);
}
@ -70,7 +58,8 @@ class SystemTagMappingNodeTest extends \Test\TestCase {
'files',
$this->user,
$this->tagManager,
$this->tagMapper
$this->tagMapper,
fn ($id): bool => in_array($id, $writableNodeIds),
);
}
@ -84,7 +73,7 @@ class SystemTagMappingNodeTest extends \Test\TestCase {
}
public function testDeleteTag(): void {
$node = $this->getMappingNode();
$node = $this->getMappingNode(null, [123]);
$this->tagManager->expects($this->once())
->method('canUserSeeTag')
->with($node->getSystemTag())
@ -102,6 +91,25 @@ class SystemTagMappingNodeTest extends \Test\TestCase {
$node->delete();
}
public function testDeleteTagForbidden(): void {
$node = $this->getMappingNode();
$this->tagManager->expects($this->once())
->method('canUserSeeTag')
->with($node->getSystemTag())
->willReturn(true);
$this->tagManager->expects($this->once())
->method('canUserAssignTag')
->with($node->getSystemTag())
->willReturn(true);
$this->tagManager->expects($this->never())
->method('deleteTags');
$this->tagMapper->expects($this->never())
->method('unassignTags');
$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
$node->delete();
}
public function tagNodeDeleteProviderPermissionException() {
return [
[
@ -144,7 +152,7 @@ class SystemTagMappingNodeTest extends \Test\TestCase {
$this->assertInstanceOf($expectedException, $thrown);
}
public function testDeleteTagNotFound(): void {
$this->expectException(\Sabre\DAV\Exception\NotFound::class);
@ -164,6 +172,6 @@ class SystemTagMappingNodeTest extends \Test\TestCase {
->with(123, 'files', 1)
->will($this->throwException(new TagNotFoundException()));
$this->getMappingNode($tag)->delete();
$this->getMappingNode($tag, [123])->delete();
}
}

@ -32,21 +32,9 @@ use OCP\SystemTag\ISystemTagObjectMapper;
use OCP\SystemTag\TagNotFoundException;
class SystemTagsObjectMappingCollectionTest extends \Test\TestCase {
/**
* @var \OCP\SystemTag\ISystemTagManager
*/
private $tagManager;
/**
* @var \OCP\SystemTag\ISystemTagObjectMapper
*/
private $tagMapper;
/**
* @var \OCP\IUser
*/
private $user;
private ISystemTagManager $tagManager;
private ISystemTagObjectMapper $tagMapper;
private IUser $user;
protected function setUp(): void {
parent::setUp();
@ -60,13 +48,14 @@ class SystemTagsObjectMappingCollectionTest extends \Test\TestCase {
->getMock();
}
public function getNode() {
public function getNode(array $writableNodeIds = []) {
return new \OCA\DAV\SystemTag\SystemTagsObjectMappingCollection(
111,
'files',
$this->user,
$this->tagManager,
$this->tagMapper
$this->tagMapper,
fn ($id): bool => in_array($id, $writableNodeIds),
);
}
@ -89,6 +78,28 @@ class SystemTagsObjectMappingCollectionTest extends \Test\TestCase {
->method('assignTags')
->with(111, 'files', '555');
$this->getNode([111])->createFile('555');
}
public function testAssignTagForbidden(): void {
$tag = new SystemTag('1', 'Test', true, true);
$this->tagManager->expects($this->once())
->method('canUserSeeTag')
->with($tag)
->willReturn(true);
$this->tagManager->expects($this->once())
->method('canUserAssignTag')
->with($tag)
->willReturn(true);
$this->tagManager->expects($this->once())
->method('getTagsByIds')
->with(['555'])
->willReturn([$tag]);
$this->tagMapper->expects($this->never())
->method('assignTags');
$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
$this->getNode()->createFile('555');
}
@ -132,7 +143,7 @@ class SystemTagsObjectMappingCollectionTest extends \Test\TestCase {
$this->assertInstanceOf($expectedException, $thrown);
}
public function testAssignTagNotFound(): void {
$this->expectException(\Sabre\DAV\Exception\PreconditionFailed::class);
@ -144,7 +155,7 @@ class SystemTagsObjectMappingCollectionTest extends \Test\TestCase {
$this->getNode()->createFile('555');
}
public function testForbiddenCreateDirectory(): void {
$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
@ -174,7 +185,7 @@ class SystemTagsObjectMappingCollectionTest extends \Test\TestCase {
$this->assertEquals('555', $childNode->getName());
}
public function testGetChildNonVisible(): void {
$this->expectException(\Sabre\DAV\Exception\NotFound::class);
@ -197,7 +208,7 @@ class SystemTagsObjectMappingCollectionTest extends \Test\TestCase {
$this->getNode()->getChild('555');
}
public function testGetChildRelationNotFound(): void {
$this->expectException(\Sabre\DAV\Exception\NotFound::class);
@ -209,7 +220,7 @@ class SystemTagsObjectMappingCollectionTest extends \Test\TestCase {
$this->getNode()->getChild('777');
}
public function testGetChildInvalidId(): void {
$this->expectException(\Sabre\DAV\Exception\BadRequest::class);
@ -221,7 +232,7 @@ class SystemTagsObjectMappingCollectionTest extends \Test\TestCase {
$this->getNode()->getChild('badid');
}
public function testGetChildTagDoesNotExist(): void {
$this->expectException(\Sabre\DAV\Exception\NotFound::class);
@ -325,7 +336,7 @@ class SystemTagsObjectMappingCollectionTest extends \Test\TestCase {
$this->assertFalse($this->getNode()->childExists('555'));
}
public function testChildExistsInvalidId(): void {
$this->expectException(\Sabre\DAV\Exception\BadRequest::class);
@ -337,14 +348,14 @@ class SystemTagsObjectMappingCollectionTest extends \Test\TestCase {
$this->getNode()->childExists('555');
}
public function testDelete(): void {
$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
$this->getNode()->delete();
}
public function testSetName(): void {
$this->expectException(\Sabre\DAV\Exception\Forbidden::class);

@ -87,6 +87,15 @@ class SystemTagsObjectTypeCollectionTest extends \Test\TestCase {
$node = $userFolder->getFirstNodeById(intval($name));
return $node !== null;
};
$writeAccessClosure = function ($name) use ($userFolder) {
$nodes = $userFolder->getById((int)$name);
foreach ($nodes as $node) {
if (($node->getPermissions() & Constants::PERMISSION_UPDATE) === Constants::PERMISSION_UPDATE) {
return true;
}
}
return false;
};
$this->node = new \OCA\DAV\SystemTag\SystemTagsObjectTypeCollection(
'files',
@ -94,7 +103,8 @@ class SystemTagsObjectTypeCollectionTest extends \Test\TestCase {
$this->tagMapper,
$userSession,
$groupManager,
$closure
$closure,
$writeAccessClosure,
);
}

@ -2,8 +2,8 @@ OC.L10N.register(
"encryption",
{
"Missing recovery key password" : "Mot de passe de la clé de récupération manquant",
"Please repeat the recovery key password" : "Répétez le mot de passe de la clef de récupération",
"Repeated recovery key password does not match the provided recovery key password" : "Le mot de passe de la clef de récupération et sa répétition ne sont pas identiques.",
"Please repeat the recovery key password" : "Répétez le mot de passe de la clé de récupération",
"Repeated recovery key password does not match the provided recovery key password" : "Le mot de passe de la clé de récupération et sa répétition ne sont pas identiques.",
"Recovery key successfully enabled" : "Clé de récupération activée avec succès",
"Could not enable recovery key. Please check your recovery key password!" : "Impossible d'activer la clé de récupération. Veuillez vérifier le mot de passe de votre clé de récupération !",
"Recovery key successfully disabled" : "Clé de récupération désactivée avec succès",
@ -15,7 +15,7 @@ OC.L10N.register(
"Password successfully changed." : "Mot de passe modifié avec succès.",
"Could not change the password. Maybe the old password was not correct." : "Erreur lors du changement de mot de passe. L'ancien mot de passe est peut-être incorrect.",
"Recovery Key disabled" : "Clé de récupération désactivée",
"Recovery Key enabled" : "Clef de récupération activée",
"Recovery Key enabled" : "Clé de récupération activée",
"Could not enable the recovery key, please try again or contact your administrator" : "Impossible d'activer la clé de récupération. Veuillez essayer à nouveau ou contacter votre administrateur",
"Could not update the private key password." : "Impossible de mettre à jour le mot de passe de la clé privée.",
"The old password was not correct, please try again." : "L'ancien mot de passe est incorrect. Veuillez réessayer.",
@ -63,6 +63,6 @@ OC.L10N.register(
"Disabled" : "Désactivé",
"Please login to the web interface, go to the \"Security\" section of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password." : "Connectez-vous depuis l'interface web, allez dans la section \"sécurité\" de vos paramètres personnels et mettez à jour votre mot de passe de chiffrement en entrant ce mot de passe dans le champ \"Ancien mot de passe de connexion\" et dans votre mot de passe de connexion actuel.",
"In order to use this encryption module you need to enable server-side\n\t\tencryption in the admin settings. Once enabled this module will encrypt\n\t\tall your files transparently. The encryption is based on AES 256 keys.\n\t\tThe module won't touch existing files, only new files will be encrypted\n\t\tafter server-side encryption was enabled. It is also not possible to\n\t\tdisable the encryption again and switch back to a unencrypted system.\n\t\tPlease read the documentation to know all implications before you decide\n\t\tto enable server-side encryption." : "Pour utiliser ce module de chiffrement, vous devez activer le chiffrement côté serveur dans les paramètres d'administration. Une fois activé, ce module chiffrera tous vos fichiers de manière transparente. Le chiffrement est basé sur des clés AES 256 bits.\nLe module ne touchera pas les fichiers existants, seuls les nouveaux fichiers seront chiffrés. Une fois le chiffrement côté serveur activé, il n'est pas possible de désactiver le chiffrement et de revenir à un système non chiffré.\n\t\tVeuillez lire la documentation pour connaître toutes les implications avant de vous décider à activer le chiffrement côté serveur.",
"The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "La clé de récupération est une clé supplémentaire utilisée pour chiffrer les fichiers. Elle permet de récupérer les fichiers des utilisateurs s'ils oublient leur mot de passe."
"The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "La clé de récupération est une clé supplémentaire utilisée pour chiffrer les fichiers. Elle permet de récupérer les fichiers des utilisateurs sils oublient leur mot de passe."
},
"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");

@ -1,7 +1,7 @@
{ "translations": {
"Missing recovery key password" : "Mot de passe de la clé de récupération manquant",
"Please repeat the recovery key password" : "Répétez le mot de passe de la clef de récupération",
"Repeated recovery key password does not match the provided recovery key password" : "Le mot de passe de la clef de récupération et sa répétition ne sont pas identiques.",
"Please repeat the recovery key password" : "Répétez le mot de passe de la clé de récupération",
"Repeated recovery key password does not match the provided recovery key password" : "Le mot de passe de la clé de récupération et sa répétition ne sont pas identiques.",
"Recovery key successfully enabled" : "Clé de récupération activée avec succès",
"Could not enable recovery key. Please check your recovery key password!" : "Impossible d'activer la clé de récupération. Veuillez vérifier le mot de passe de votre clé de récupération !",
"Recovery key successfully disabled" : "Clé de récupération désactivée avec succès",
@ -13,7 +13,7 @@
"Password successfully changed." : "Mot de passe modifié avec succès.",
"Could not change the password. Maybe the old password was not correct." : "Erreur lors du changement de mot de passe. L'ancien mot de passe est peut-être incorrect.",
"Recovery Key disabled" : "Clé de récupération désactivée",
"Recovery Key enabled" : "Clef de récupération activée",
"Recovery Key enabled" : "Clé de récupération activée",
"Could not enable the recovery key, please try again or contact your administrator" : "Impossible d'activer la clé de récupération. Veuillez essayer à nouveau ou contacter votre administrateur",
"Could not update the private key password." : "Impossible de mettre à jour le mot de passe de la clé privée.",
"The old password was not correct, please try again." : "L'ancien mot de passe est incorrect. Veuillez réessayer.",
@ -61,6 +61,6 @@
"Disabled" : "Désactivé",
"Please login to the web interface, go to the \"Security\" section of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password." : "Connectez-vous depuis l'interface web, allez dans la section \"sécurité\" de vos paramètres personnels et mettez à jour votre mot de passe de chiffrement en entrant ce mot de passe dans le champ \"Ancien mot de passe de connexion\" et dans votre mot de passe de connexion actuel.",
"In order to use this encryption module you need to enable server-side\n\t\tencryption in the admin settings. Once enabled this module will encrypt\n\t\tall your files transparently. The encryption is based on AES 256 keys.\n\t\tThe module won't touch existing files, only new files will be encrypted\n\t\tafter server-side encryption was enabled. It is also not possible to\n\t\tdisable the encryption again and switch back to a unencrypted system.\n\t\tPlease read the documentation to know all implications before you decide\n\t\tto enable server-side encryption." : "Pour utiliser ce module de chiffrement, vous devez activer le chiffrement côté serveur dans les paramètres d'administration. Une fois activé, ce module chiffrera tous vos fichiers de manière transparente. Le chiffrement est basé sur des clés AES 256 bits.\nLe module ne touchera pas les fichiers existants, seuls les nouveaux fichiers seront chiffrés. Une fois le chiffrement côté serveur activé, il n'est pas possible de désactiver le chiffrement et de revenir à un système non chiffré.\n\t\tVeuillez lire la documentation pour connaître toutes les implications avant de vous décider à activer le chiffrement côté serveur.",
"The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "La clé de récupération est une clé supplémentaire utilisée pour chiffrer les fichiers. Elle permet de récupérer les fichiers des utilisateurs s'ils oublient leur mot de passe."
"The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "La clé de récupération est une clé supplémentaire utilisée pour chiffrer les fichiers. Elle permet de récupérer les fichiers des utilisateurs sils oublient leur mot de passe."
},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}

@ -10,7 +10,7 @@ OC.L10N.register(
"Server to server sharing is not enabled on this server" : "Bu sunucuda sunucudan sunucuya paylaşım etkin değil",
"Couldn't establish a federated share." : "Birleşik bir paylaşım oluşturulamadı.",
"Couldn't establish a federated share, maybe the password was wrong." : "Birleşik bir paylaşım oluşturulamadı. Parola yanlış olabilir.",
"Federated Share request sent, you will receive an invitation. Check your notifications." : "Birleşik paylaşım isteği gönderildi. Bir çağrı alacaksınız. Bildirimlerinizi denetleyin.",
"Federated Share request sent, you will receive an invitation. Check your notifications." : "Birleşik paylaşım isteği gönderildi. Bir davet alacaksınız. Bildirimlerinizi denetleyin.",
"Couldn't establish a federated share, it looks like the server to federate with is too old (Nextcloud <= 9)." : "Birleşik bir paylaşım oluşturulamadı. Birleşik çalışılacak sunucunun sürümü çok eski gibi görünüyor (Nextcloud <= 9).",
"It is not allowed to send federated group shares from this server." : "Bu sunucudan birleşik grup paylaşımları gönderilemez",
"Sharing %1$s failed, because this item is already shared with the account %2$s" : "%1$s paylaşılamadı. Bu öge zaten %2$s hesabı ile paylaşılmış",

@ -8,7 +8,7 @@
"Server to server sharing is not enabled on this server" : "Bu sunucuda sunucudan sunucuya paylaşım etkin değil",
"Couldn't establish a federated share." : "Birleşik bir paylaşım oluşturulamadı.",
"Couldn't establish a federated share, maybe the password was wrong." : "Birleşik bir paylaşım oluşturulamadı. Parola yanlış olabilir.",
"Federated Share request sent, you will receive an invitation. Check your notifications." : "Birleşik paylaşım isteği gönderildi. Bir çağrı alacaksınız. Bildirimlerinizi denetleyin.",
"Federated Share request sent, you will receive an invitation. Check your notifications." : "Birleşik paylaşım isteği gönderildi. Bir davet alacaksınız. Bildirimlerinizi denetleyin.",
"Couldn't establish a federated share, it looks like the server to federate with is too old (Nextcloud <= 9)." : "Birleşik bir paylaşım oluşturulamadı. Birleşik çalışılacak sunucunun sürümü çok eski gibi görünüyor (Nextcloud <= 9).",
"It is not allowed to send federated group shares from this server." : "Bu sunucudan birleşik grup paylaşımları gönderilemez",
"Sharing %1$s failed, because this item is already shared with the account %2$s" : "%1$s paylaşılamadı. Bu öge zaten %2$s hesabı ile paylaşılmış",

@ -94,7 +94,7 @@ class Notifier implements INotifier {
}
// Read the language from the notification
$l = $this->factory->get('files_sharing', $languageCode);
$l = $this->factory->get('federatedfilesharing', $languageCode);
switch ($notification->getSubject()) {
// Deal with known subjects

@ -195,9 +195,9 @@ class FederatedShareProviderTest extends \Test\TestCase {
$this->equalTo('myFile'),
$this->anything(),
'shareOwner',
'shareOwner@http://localhost/',
'shareOwner@http://localhost',
'sharedBy',
'sharedBy@http://localhost/'
'sharedBy@http://localhost'
)
->willReturn(true);
@ -276,9 +276,9 @@ class FederatedShareProviderTest extends \Test\TestCase {
$this->equalTo('myFile'),
$this->anything(),
'shareOwner',
'shareOwner@http://localhost/',
'shareOwner@http://localhost',
'sharedBy',
'sharedBy@http://localhost/'
'sharedBy@http://localhost'
)->willReturn(false);
$this->rootFolder->method('getById')
@ -337,9 +337,9 @@ class FederatedShareProviderTest extends \Test\TestCase {
$this->equalTo('myFile'),
$this->anything(),
'shareOwner',
'shareOwner@http://localhost/',
'shareOwner@http://localhost',
'sharedBy',
'sharedBy@http://localhost/'
'sharedBy@http://localhost'
)->willThrowException(new \Exception('dummy'));
$this->rootFolder->method('getById')
@ -443,9 +443,9 @@ class FederatedShareProviderTest extends \Test\TestCase {
$this->equalTo('myFile'),
$this->anything(),
'shareOwner',
'shareOwner@http://localhost/',
'shareOwner@http://localhost',
'sharedBy',
'sharedBy@http://localhost/'
'sharedBy@http://localhost'
)->willReturn(true);
$this->rootFolder->expects($this->never())->method($this->anything());
@ -514,9 +514,9 @@ class FederatedShareProviderTest extends \Test\TestCase {
$this->equalTo('myFile'),
$this->anything(),
$owner,
$owner . '@http://localhost/',
$owner . '@http://localhost',
$sharedBy,
$sharedBy . '@http://localhost/'
$sharedBy . '@http://localhost'
)->willReturn(true);
if ($owner === $sharedBy) {

@ -4,7 +4,7 @@ OC.L10N.register(
"Added to the list of trusted servers" : "Ajouté à la liste des serveurs de confiance",
"Server is already in the list of trusted servers." : "Le serveur est déjà dans la liste des serveurs de confiance.",
"No server to federate with found" : "Aucun serveur avec lequel fédérer n'a été trouvé",
"Could not add server" : "Impossible d'ajouter le serveur",
"Could not add server" : "Impossible dajouter le serveur",
"Trusted servers" : "Serveurs de confiance",
"Federation" : "Fédération",
"Federation allows you to connect with other trusted servers to exchange the account directory." : "Une fédération vous permet de vous connecter avec d'autres serveurs de confiance pour échanger la liste des comptes.",

@ -2,7 +2,7 @@
"Added to the list of trusted servers" : "Ajouté à la liste des serveurs de confiance",
"Server is already in the list of trusted servers." : "Le serveur est déjà dans la liste des serveurs de confiance.",
"No server to federate with found" : "Aucun serveur avec lequel fédérer n'a été trouvé",
"Could not add server" : "Impossible d'ajouter le serveur",
"Could not add server" : "Impossible dajouter le serveur",
"Trusted servers" : "Serveurs de confiance",
"Federation" : "Fédération",
"Federation allows you to connect with other trusted servers to exchange the account directory." : "Une fédération vous permet de vous connecter avec d'autres serveurs de confiance pour échanger la liste des comptes.",

@ -148,7 +148,7 @@ OC.L10N.register(
"You moved {oldfile} to {newfile}" : "شما {oldfile} را به {newfile} منتقل کردید",
"{user} moved {oldfile} to {newfile}" : "{user} {oldfile} را به {newfile} منتقل کرد",
"A file has been added to or removed from your <strong>favorites</strong>" : "یک فایل به موارد دلخواه شما اضافه یا حذف شده است",
"A file or folder has been <strong>changed</strong>" : "پرونده یا پوشه‌ای به <strong>تغییر</strong> داده شد",
"A file or folder has been <strong>changed</strong>" : "یک فایل یا پوشه تغییر کرده است",
"A favorite file or folder has been <strong>changed</strong>" : "یک فایل یا پوشه مورد علاقه تغییر کرده است",
"Upload (max. %s)" : "آپلود (بیشترین سایز %s)",
"Accept" : "قبول",
@ -276,7 +276,7 @@ OC.L10N.register(
"Deleted files" : "پرونده‌های حذف شده",
"Shares" : "اشتراک گذاری ها",
"Shared with others" : "موارد به اشتراک گذاشته شده با دیگران",
"Shared with you" : "Shared with you",
"Shared with you" : "به اشتراک گذاشته شده با شما",
"Deleted shares" : "اشتراک گذاری های حذف شده",
"Pending shares" : "اشتراک در حال انتظار ",
"This file has the tag {tag}" : "این فایل دارای تگ {tag} است",

@ -146,7 +146,7 @@
"You moved {oldfile} to {newfile}" : "شما {oldfile} را به {newfile} منتقل کردید",
"{user} moved {oldfile} to {newfile}" : "{user} {oldfile} را به {newfile} منتقل کرد",
"A file has been added to or removed from your <strong>favorites</strong>" : "یک فایل به موارد دلخواه شما اضافه یا حذف شده است",
"A file or folder has been <strong>changed</strong>" : "پرونده یا پوشه‌ای به <strong>تغییر</strong> داده شد",
"A file or folder has been <strong>changed</strong>" : "یک فایل یا پوشه تغییر کرده است",
"A favorite file or folder has been <strong>changed</strong>" : "یک فایل یا پوشه مورد علاقه تغییر کرده است",
"Upload (max. %s)" : "آپلود (بیشترین سایز %s)",
"Accept" : "قبول",
@ -274,7 +274,7 @@
"Deleted files" : "پرونده‌های حذف شده",
"Shares" : "اشتراک گذاری ها",
"Shared with others" : "موارد به اشتراک گذاشته شده با دیگران",
"Shared with you" : "Shared with you",
"Shared with you" : "به اشتراک گذاشته شده با شما",
"Deleted shares" : "اشتراک گذاری های حذف شده",
"Pending shares" : "اشتراک در حال انتظار ",
"This file has the tag {tag}" : "این فایل دارای تگ {tag} است",

@ -12,11 +12,11 @@ OC.L10N.register(
"Close" : "Fermer",
"Could not create folder \"{dir}\"" : "Impossible de créer le dossier \"{dir}\"",
"This will stop your current uploads." : "Cela va arrêter vos envois en cours.",
"Upload cancelled." : "Envoi annulé.",
"Upload cancelled." : "Téléversement annulé.",
"Processing files …" : "Fichiers en cours de traitement …",
"…" : "…",
"Unable to upload {filename} as it is a directory or has 0 bytes" : "Impossible de téléverser {filename} car il s'agit d'un dossier ou d'un fichier vide",
"Not enough free space, you are uploading {size1} but only {size2} is left" : "Espace libre insuffisant : vous tentez d'envoyer {size1} mais seulement {size2} sont disponibles",
"Not enough free space, you are uploading {size1} but only {size2} is left" : "Espace libre insuffisant : vous tentez denvoyer {size1} mais seulement {size2} sont disponibles",
"Target folder \"{dir}\" does not exist any more" : "Le dossier cible \"{dir}\" n'existe plus",
"Not enough free space" : "Espace disponible insuffisant",
"An unknown error has occurred" : "Une erreur inconnue est survenue",
@ -63,9 +63,9 @@ OC.L10N.register(
"Failed to redirect to client" : "Échec de la redirection vers le client",
"{newName} already exists" : "{newName} existe déjà",
"Could not rename \"{fileName}\", it does not exist any more" : "Impossible de renommer \"{fileName}\" car il n'existe plus",
"The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Le nom \"{targetName}\" est déjà utilisé dans le dossier \"{dir}\". Merci de choisir un nom différent.",
"The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Le nom « {targetName} » est déjà utilisé dans le dossier « {dir} ». Merci de choisir un nom différent.",
"Could not rename \"{fileName}\"" : "Impossible de renommer « {fileName} »",
"Could not create file \"{file}\"" : "Impossible de créer le fichier \"{file}\"",
"Could not create file \"{file}\"" : "Impossible de créer le fichier « {file} »",
"Could not create file \"{file}\" because it already exists" : "Impossible de créer le fichier « {file} » car il existe déjà",
"Could not create folder \"{dir}\" because it already exists" : "Impossible de créer le dossier \"{dir}\" car il existe déjà",
"Could not fetch file details \"{file}\"" : "Impossible de récupérer les détails du fichier \"{file}\"",
@ -87,7 +87,7 @@ OC.L10N.register(
"{used}%" : "{used}%",
"{used} of {quota} used" : "{used} utilisés sur {quota}",
"{used} used" : "{used} utilisés",
"\"{name}\" is an invalid file name." : "\"{name}\" n'est pas un nom de fichier valide.",
"\"{name}\" is an invalid file name." : "« {name} » nest pas un nom de fichier valide.",
"File name cannot be empty." : "Le nom de fichier ne peut pas être vide.",
"\"/\" is not allowed inside a file name." : "\"/\" n'est pas autorisé dans un nom de fichier.",
"\"{name}\" is not an allowed filetype" : "\"{name}\" n'est pas un type de fichier autorisé",
@ -319,7 +319,7 @@ OC.L10N.register(
"No entries found in this folder" : "Aucune entrée trouvée dans ce dossier",
"Select all" : "Tout sélectionner",
"Upload too large" : "Données envoyées trop volumineuses",
"The files you are trying to upload exceed the maximum size for file uploads on this server." : "Les fichiers que vous essayez d'envoyer dépassent la taille maximale d'envoi permise par ce serveur.",
"The files you are trying to upload exceed the maximum size for file uploads on this server." : "Les fichiers que vous essayez denvoyer dépassent la taille maximale denvoi permise par ce serveur.",
"Text file" : "Fichier texte",
"New text file.txt" : "Nouveau fichier texte.txt",
"Direct link was copied (only works for users who have access to this file/folder)" : "Le lien direct a été copié (fonctionne uniquement pour les utilisateurs qui ont accès à ce fichier/dossier)",

@ -10,11 +10,11 @@
"Close" : "Fermer",
"Could not create folder \"{dir}\"" : "Impossible de créer le dossier \"{dir}\"",
"This will stop your current uploads." : "Cela va arrêter vos envois en cours.",
"Upload cancelled." : "Envoi annulé.",
"Upload cancelled." : "Téléversement annulé.",
"Processing files …" : "Fichiers en cours de traitement …",
"…" : "…",
"Unable to upload {filename} as it is a directory or has 0 bytes" : "Impossible de téléverser {filename} car il s'agit d'un dossier ou d'un fichier vide",
"Not enough free space, you are uploading {size1} but only {size2} is left" : "Espace libre insuffisant : vous tentez d'envoyer {size1} mais seulement {size2} sont disponibles",
"Not enough free space, you are uploading {size1} but only {size2} is left" : "Espace libre insuffisant : vous tentez denvoyer {size1} mais seulement {size2} sont disponibles",
"Target folder \"{dir}\" does not exist any more" : "Le dossier cible \"{dir}\" n'existe plus",
"Not enough free space" : "Espace disponible insuffisant",
"An unknown error has occurred" : "Une erreur inconnue est survenue",
@ -61,9 +61,9 @@
"Failed to redirect to client" : "Échec de la redirection vers le client",
"{newName} already exists" : "{newName} existe déjà",
"Could not rename \"{fileName}\", it does not exist any more" : "Impossible de renommer \"{fileName}\" car il n'existe plus",
"The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Le nom \"{targetName}\" est déjà utilisé dans le dossier \"{dir}\". Merci de choisir un nom différent.",
"The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Le nom « {targetName} » est déjà utilisé dans le dossier « {dir} ». Merci de choisir un nom différent.",
"Could not rename \"{fileName}\"" : "Impossible de renommer « {fileName} »",
"Could not create file \"{file}\"" : "Impossible de créer le fichier \"{file}\"",
"Could not create file \"{file}\"" : "Impossible de créer le fichier « {file} »",
"Could not create file \"{file}\" because it already exists" : "Impossible de créer le fichier « {file} » car il existe déjà",
"Could not create folder \"{dir}\" because it already exists" : "Impossible de créer le dossier \"{dir}\" car il existe déjà",
"Could not fetch file details \"{file}\"" : "Impossible de récupérer les détails du fichier \"{file}\"",
@ -85,7 +85,7 @@
"{used}%" : "{used}%",
"{used} of {quota} used" : "{used} utilisés sur {quota}",
"{used} used" : "{used} utilisés",
"\"{name}\" is an invalid file name." : "\"{name}\" n'est pas un nom de fichier valide.",
"\"{name}\" is an invalid file name." : "« {name} » nest pas un nom de fichier valide.",
"File name cannot be empty." : "Le nom de fichier ne peut pas être vide.",
"\"/\" is not allowed inside a file name." : "\"/\" n'est pas autorisé dans un nom de fichier.",
"\"{name}\" is not an allowed filetype" : "\"{name}\" n'est pas un type de fichier autorisé",
@ -317,7 +317,7 @@
"No entries found in this folder" : "Aucune entrée trouvée dans ce dossier",
"Select all" : "Tout sélectionner",
"Upload too large" : "Données envoyées trop volumineuses",
"The files you are trying to upload exceed the maximum size for file uploads on this server." : "Les fichiers que vous essayez d'envoyer dépassent la taille maximale d'envoi permise par ce serveur.",
"The files you are trying to upload exceed the maximum size for file uploads on this server." : "Les fichiers que vous essayez denvoyer dépassent la taille maximale denvoi permise par ce serveur.",
"Text file" : "Fichier texte",
"New text file.txt" : "Nouveau fichier texte.txt",
"Direct link was copied (only works for users who have access to this file/folder)" : "Le lien direct a été copié (fonctionne uniquement pour les utilisateurs qui ont accès à ce fichier/dossier)",

@ -241,6 +241,7 @@ OC.L10N.register(
"Files settings" : "Dosyalar ayarları",
"File cannot be accessed" : "Dosyaya erişilemedi",
"The file could not be found or you do not have permissions to view it. Ask the sender to share it." : "Dosya bulunamadı ya da görüntüleme izniniz olmayabilir. Gönderenden paylaşmasını isteyin",
"Your files" : "Dosyalarınız",
"Open in files" : "Dosyalar uygulamasında aç",
"Sort favorites first" : "Sık kullanılanlar üstte sıralansın",
"Sort folders before files" : "Klasörler dosyaların üzerinde sıralansın",
@ -273,7 +274,9 @@ OC.L10N.register(
"You cannot move a file/folder onto itself or into a subfolder of itself" : "Bir dosyayı ya da klasörü kendi üzerine veya kendisinin alt klasörüne taşıyamazsınız",
"(copy)" : "(kopya)",
"(copy %n)" : "(%n kopyası)",
"Move cancelled" : "Taşıma iptal edildi",
"A file or folder with that name already exists in this folder" : "Bu klasörde aynı adlı bir dosya ya da klasör zaten var",
"The files are locked" : "Dosyalar kilitli",
"The file does not exist anymore" : "Dosya artık yok",
"Choose destination" : "Hedefi seçin",
"Copy to {target}" : "{target} içine kopyala",
@ -292,6 +295,8 @@ OC.L10N.register(
"Create new templates folder" : "Yeni kalıp klasörü oluştur",
"Templates" : "Kalıplar",
"New template folder" : "Yeni kalıp klasörü",
"In folder" : "Klasörde",
"Search in folder: {folder}" : "Şu klasörde ara: {folder}",
"One of the dropped files could not be processed" : "Bırakılan dosyalardan biri işlenemedi",
"Uploading \"{filename}\" failed" : "\"{filename}\" yüklenemedi",
"_{folderCount} folder_::_{folderCount} folders_" : ["{folderCount} klasör","{folderCount} klasör"],

@ -239,6 +239,7 @@
"Files settings" : "Dosyalar ayarları",
"File cannot be accessed" : "Dosyaya erişilemedi",
"The file could not be found or you do not have permissions to view it. Ask the sender to share it." : "Dosya bulunamadı ya da görüntüleme izniniz olmayabilir. Gönderenden paylaşmasını isteyin",
"Your files" : "Dosyalarınız",
"Open in files" : "Dosyalar uygulamasında aç",
"Sort favorites first" : "Sık kullanılanlar üstte sıralansın",
"Sort folders before files" : "Klasörler dosyaların üzerinde sıralansın",
@ -271,7 +272,9 @@
"You cannot move a file/folder onto itself or into a subfolder of itself" : "Bir dosyayı ya da klasörü kendi üzerine veya kendisinin alt klasörüne taşıyamazsınız",
"(copy)" : "(kopya)",
"(copy %n)" : "(%n kopyası)",
"Move cancelled" : "Taşıma iptal edildi",
"A file or folder with that name already exists in this folder" : "Bu klasörde aynı adlı bir dosya ya da klasör zaten var",
"The files are locked" : "Dosyalar kilitli",
"The file does not exist anymore" : "Dosya artık yok",
"Choose destination" : "Hedefi seçin",
"Copy to {target}" : "{target} içine kopyala",
@ -290,6 +293,8 @@
"Create new templates folder" : "Yeni kalıp klasörü oluştur",
"Templates" : "Kalıplar",
"New template folder" : "Yeni kalıp klasörü",
"In folder" : "Klasörde",
"Search in folder: {folder}" : "Şu klasörde ara: {folder}",
"One of the dropped files could not be processed" : "Bırakılan dosyalardan biri işlenemedi",
"Uploading \"{filename}\" failed" : "\"{filename}\" yüklenemedi",
"_{folderCount} folder_::_{folderCount} folders_" : ["{folderCount} klasör","{folderCount} klasör"],

@ -44,6 +44,7 @@ namespace OCA\Files;
* label: string,
* extension: string,
* iconClass: ?string,
* iconSvgInline: ?string,
* mimetypes: string[],
* ratio: ?float,
* actionLabel: string,

@ -151,6 +151,7 @@
"label",
"extension",
"iconClass",
"iconSvgInline",
"mimetypes",
"ratio",
"actionLabel"
@ -169,6 +170,10 @@
"type": "string",
"nullable": true
},
"iconSvgInline": {
"type": "string",
"nullable": true
},
"mimetypes": {
"type": "array",
"items": {

@ -28,6 +28,12 @@ const view = {
name: 'Files',
} as View
// Mock webroot variable
beforeAll(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any)._oc_webroot = ''
})
describe('Download action conditions tests', () => {
test('Default values', () => {
expect(action).toBeInstanceOf(FileAction)

@ -30,6 +30,12 @@ const view = {
name: 'Files',
} as View
// Mock webroot variable
beforeAll(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any)._oc_webroot = ''
})
describe('Edit locally action conditions tests', () => {
test('Default values', () => {
expect(action).toBeInstanceOf(FileAction)

@ -41,6 +41,12 @@ global.window.OC = {
TAG_FAVORITE: '_$!<Favorite>!$_',
}
// Mock webroot variable
beforeAll(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any)._oc_webroot = ''
})
describe('Favorite action conditions tests', () => {
test('Default values', () => {
const file = new File({

@ -120,7 +120,14 @@ export const handleCopyMoveNodeTo = async (node: Node, destination: Folder, meth
// If we do not allow overwriting then find an unique name
if (!overwrite) {
const otherNodes = await client.getDirectoryContents(destinationPath) as FileStat[]
target = getUniqueName(node.basename, otherNodes.map((n) => n.basename), copySuffix)
target = getUniqueName(
node.basename,
otherNodes.map((n) => n.basename),
{
suffix: copySuffix,
ignoreFileExtension: node.type === FileType.Folder,
},
)
}
await client.copyFile(currentPath, join(destinationPath, target))
// If the node is copied into current directory the view needs to be updated
@ -150,7 +157,7 @@ export const handleCopyMoveNodeTo = async (node: Node, destination: Folder, meth
}
} catch (error) {
// User cancelled
showError(t('files','Move cancelled'))
showError(t('files', 'Move cancelled'))
return
}
}
@ -212,7 +219,7 @@ const openFilePickerForAction = async (action: MoveCopyAction, dir = '/', nodes:
if (action === MoveCopyAction.COPY || action === MoveCopyAction.MOVE_OR_COPY) {
buttons.push({
label: target ? t('files', 'Copy to {target}', { target }) : t('files', 'Copy'),
label: target ? t('files', 'Copy to {target}', { target }, undefined, { escape: false, sanitize: false }) : t('files', 'Copy'),
type: 'primary',
icon: CopyIconSvg,
async callback(destination: Node[]) {
@ -237,7 +244,7 @@ const openFilePickerForAction = async (action: MoveCopyAction, dir = '/', nodes:
if (action === MoveCopyAction.MOVE || action === MoveCopyAction.MOVE_OR_COPY) {
buttons.push({
label: target ? t('files', 'Move to {target}', { target }) : t('files', 'Move'),
label: target ? t('files', 'Move to {target}', { target }, undefined, { escape: false, sanitize: false }) : t('files', 'Move'),
type: action === MoveCopyAction.MOVE ? 'primary' : 'secondary',
icon: FolderMoveSvg,
async callback(destination: Node[]) {

@ -22,6 +22,7 @@
-->
<template>
<div v-show="dragover"
data-cy-files-drag-drop-area
class="files-list__drag-drop-notice"
@drop="onDrop">
<div class="files-list__drag-drop-notice-wrapper">

@ -64,8 +64,8 @@ export function registerTemplateEntries() {
addNewFileMenuEntry({
id: `template-new-${provider.app}-${index}`,
displayName: provider.label,
// TODO: migrate to inline svg
iconClass: provider.iconClass || 'icon-file',
iconSvgInline: provider.iconSvgInline,
enabled(context: Folder): boolean {
return (context.permissions & Permission.CREATE) !== 0
},

@ -36,21 +36,27 @@ export const handleDrop = async (data: DataTransfer): Promise<Upload[]> => {
// TODO: Maybe handle `getAsFileSystemHandle()` in the future
const uploads = [] as Upload[]
for (const item of data.items) {
if (item.kind !== 'file') {
logger.debug('Skipping dropped item', { kind: item.kind, type: item.type })
continue
}
// MDN recommends to try both, as it might be renamed in the future
const entry = (item as unknown as { getAsEntry?: () => FileSystemEntry|undefined})?.getAsEntry?.() ?? item.webkitGetAsEntry()
// we need to cache the entries to prevent Blink engine bug that clears the list (`data.items`) after first access props of one of the entries
const entries = [...data.items]
.filter((item) => {
if (item.kind !== 'file') {
logger.debug('Skipping dropped item', { kind: item.kind, type: item.type })
return false
}
return true
})
.map((item) => {
// MDN recommends to try both, as it might be renamed in the future
return (item as unknown as { getAsEntry?: () => FileSystemEntry|undefined})?.getAsEntry?.() ?? item.webkitGetAsEntry() ?? item
})
for (const entry of entries) {
// Handle browser issues if Filesystem API is not available. Fallback to File API
if (entry === null) {
if (entry instanceof DataTransferItem) {
logger.debug('Could not get FilesystemEntry of item, falling back to file')
const file = item.getAsFile()
const file = entry.getAsFile()
if (file === null) {
logger.warn('Could not process DataTransferItem', { type: item.type, kind: item.kind })
logger.warn('Could not process DataTransferItem', { type: entry.type, kind: entry.kind })
showError(t('files', 'One of the dropped files could not be processed'))
} else {
uploads.push(await handleFileUpload(file))

@ -117,6 +117,7 @@ export interface TemplateFile {
label: string
extension: string
iconClass?: string
iconSvgInline?: string
mimetypes: string[]
ratio?: number
templates?: Record<string, unknown>[]

@ -12,7 +12,7 @@
*
* 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
* 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
@ -28,15 +28,31 @@ import { translate as t, translatePlural as n } from '@nextcloud/l10n'
* Create an unique file name
* @param name The initial name to use
* @param otherNames Other names that are already used
* @param suffix A function that takes an index an returns a suffix to add, defaults to '(index)'
* @param options Optional parameters for tuning the behavior
* @param options.suffix A function that takes an index and returns a suffix to add to the file name, defaults to '(index)'
* @param options.ignoreFileExtension Set to true to ignore the file extension when adding the suffix (when getting a unique directory name)
* @return Either the initial name, if unique, or the name with the suffix so that the name is unique
*/
export const getUniqueName = (name: string, otherNames: string[], suffix = (n: number) => `(${n})`): string => {
export const getUniqueName = (
name: string,
otherNames: string[],
options: {
suffix?: (i: number) => string,
ignoreFileExtension?: boolean,
} = {},
): string => {
const opts = {
suffix: (n: number) => `(${n})`,
ignoreFileExtension: false,
...options,
}
let newName = name
let i = 1
while (otherNames.includes(newName)) {
const ext = extname(name)
newName = `${basename(name, ext)} ${suffix(i++)}${ext}`
const ext = opts.ignoreFileExtension ? '' : extname(name)
const base = basename(name, ext)
newName = `${base} ${opts.suffix(i++)}${ext}`
}
return newName
}

@ -1327,9 +1327,6 @@ MountConfigListView.prototype = _.extend({
}
if (typeof message === 'string') {
$statusSpan.attr('title', message);
$statusSpan.tooltip();
} else {
$statusSpan.tooltip('dispose');
}
},

@ -1,7 +1,7 @@
OC.L10N.register(
"files_external",
{
"Grant access" : "Autoriser l'accès",
"Grant access" : "Autoriser laccès",
"Error configuring OAuth1" : "Erreur lors de la configuration de OAuth1",
"Please provide a valid app key and secret." : "Veuillez fournir une clé d'application et un mot de passe valides.",
"Error configuring OAuth2" : "Erreur lors de la configuration de OAuth2",
@ -72,7 +72,7 @@ OC.L10N.register(
"Kerberos ticket" : "Ticket Kerberos",
"Amazon S3" : "Amazon S3",
"Bucket" : "Bucket",
"Hostname" : "Nom de l'hôte",
"Hostname" : "Nom de lhôte",
"Port" : "Port",
"Region" : "Région",
"Storage Class" : "Classe de stockage",
@ -152,7 +152,7 @@ OC.L10N.register(
"external-storage" : "external-storage",
"Couldn't fetch list of Windows network drive mount points: Empty response from server" : "Impossible d'aller chercher la liste des points de montage des disques réseaux Windows : Réponse vide du serveur",
"Please enter the credentials for the {mount} mount" : "Veuillez entrer les identifiants pour le montage {mount}",
"Username" : "Nom d'utilisateur",
"Username" : "Nom dutilisateur",
"Credentials saved" : "Identifiants sauvegardés",
"Credentials saving failed" : "La sauvegarde des identifiants a échoué",
"Credentials required" : "Identifiants exigés",

@ -1,5 +1,5 @@
{ "translations": {
"Grant access" : "Autoriser l'accès",
"Grant access" : "Autoriser laccès",
"Error configuring OAuth1" : "Erreur lors de la configuration de OAuth1",
"Please provide a valid app key and secret." : "Veuillez fournir une clé d'application et un mot de passe valides.",
"Error configuring OAuth2" : "Erreur lors de la configuration de OAuth2",
@ -70,7 +70,7 @@
"Kerberos ticket" : "Ticket Kerberos",
"Amazon S3" : "Amazon S3",
"Bucket" : "Bucket",
"Hostname" : "Nom de l'hôte",
"Hostname" : "Nom de lhôte",
"Port" : "Port",
"Region" : "Région",
"Storage Class" : "Classe de stockage",
@ -150,7 +150,7 @@
"external-storage" : "external-storage",
"Couldn't fetch list of Windows network drive mount points: Empty response from server" : "Impossible d'aller chercher la liste des points de montage des disques réseaux Windows : Réponse vide du serveur",
"Please enter the credentials for the {mount} mount" : "Veuillez entrer les identifiants pour le montage {mount}",
"Username" : "Nom d'utilisateur",
"Username" : "Nom dutilisateur",
"Credentials saved" : "Identifiants sauvegardés",
"Credentials saving failed" : "La sauvegarde des identifiants a échoué",
"Credentials required" : "Identifiants exigés",

@ -79,6 +79,7 @@ OC.L10N.register(
"Enable SSL" : "SSL kullanılsın",
"Enable Path Style" : "Yol biçemi kullanılsın",
"Legacy (v2) authentication" : "Eski (v2) kimlik doğrulama",
"Enable multipart copy" : "Çok parçalı kopya kullanılsın",
"WebDAV" : "WebDAV",
"URL" : "Adres",
"Remote subfolder" : "Uzak alt klasör",

@ -77,6 +77,7 @@
"Enable SSL" : "SSL kullanılsın",
"Enable Path Style" : "Yol biçemi kullanılsın",
"Legacy (v2) authentication" : "Eski (v2) kimlik doğrulama",
"Enable multipart copy" : "Çok parçalı kopya kullanılsın",
"WebDAV" : "WebDAV",
"URL" : "Adres",
"Remote subfolder" : "Uzak alt klasör",

@ -29,10 +29,7 @@ use OCP\IUserSession;
class DummyUserSession implements IUserSession {
/**
* @var IUser
*/
private $user;
private ?IUser $user = null;
public function login($uid, $password) {
}
@ -44,6 +41,10 @@ class DummyUserSession implements IUserSession {
$this->user = $user;
}
public function setVolatileActiveUser(?IUser $user): void {
$this->user = $user;
}
public function getUser() {
return $this->user;
}

@ -113,7 +113,7 @@ OC.L10N.register(
"You received {share} as a share by {user}" : "لقد تلقيت أنت {share} كمشاركة من قِبَل {user}",
"You received {share} to group {group} as a share by {user}" : "لقد تلقيت أنت {share} كمشاركة ضمن المجموعة {group} من قِبَل {user}",
"Accept" : "قبول",
"Reject" : "رفض",
"Decline" : "رفض",
"This application enables people to share files within Nextcloud. If enabled, the admin can choose which groups can share files. The applicable people can then share files and folders with other accounts and groups within Nextcloud. In addition, if the admin enables the share link feature, an external link can be used to share files with other people outside of Nextcloud. Admins can also enforce passwords, expirations dates, and enable server to server sharing via share links, as well as sharing from mobile devices.\nTurning the feature off removes shared files and folders on the server for all share recipients, and also on the sync clients and mobile apps. More information is available in the Nextcloud Documentation." : "يُمكِّن هذا التطبيق الأشخاص من مشاركة الملفات داخل نكست كلاود. عند تمكينه، يُمكِّن المشرف تحديد المجموعات التي يمكنها مشاركة الملفات. يمكن للأشخاص المعنيين بعد ذلك مشاركة الملفات والمجلدات مع حسابات ومجموعات أخرى داخل نكست كلاود. بالإضافة إلى ذلك، إذا قام المشرف بتمكين ميزة رابط المشاركة، فيمكن استخدام رابط خارجي لمشاركة الملفات مع أشخاص آخرين خارج نكست كلاود. يمكن للمشرفين أيضًا فرض كلمات المرور و تواريخ انتهاء الصلاحية و تمكين المشاركة من خادوم إلى خادوم عبر روابط المشاركة؛ بالإضافة إلى المشاركة من الأجهزة المحمولة. بالمقابل، يؤدي تعطيل التطبيق إلى إزالة الملفات والمجلدات المشتركة على الخادوم لدي جميع مستلمي المشاركة، و كذلك على عملاء المزامنة و تطبيقات الأجهزة المحمولة. \nللمزيد من المعلومات، أنظُر توثيق نكست كلاود.",
"Sharing" : "المشاركة",
"Accept shares from other accounts and groups by default" : "إقبَل المشاركات من حسابات و مجموعات أخرى بشكل تلقائي ",
@ -295,6 +295,7 @@ OC.L10N.register(
"Please specify a valid user" : "يرجى تحديد مستخدم صحيح",
"Please specify a valid federated user ID" : "رجاءً عيّن مُعرِّفاً صحيحاً لمستخدم اتحادي federated user ID",
"Sharing %s failed because the back end does not support sciencemesh shares" : "مشاركة %s أخفقت بسبب أن الخادوم لا يدعم مشاركات ScienceMesh",
"Reject" : "رفض",
"This application enables users to share files within Nextcloud. If enabled, the admin can choose which groups can share files. The applicable users can then share files and folders with other users and groups within Nextcloud. In addition, if the admin enables the share link feature, an external link can be used to share files with other users outside of Nextcloud. Admins can also enforce passwords, expirations dates, and enable server to server sharing via share links, as well as sharing from mobile devices.\nTurning the feature off removes shared files and folders on the server for all share recipients, and also on the sync clients and mobile apps. More information is available in the Nextcloud Documentation." : "يتيح هذا التطبيق للمستخدمين مشاركة الملفات داخل نكست كلاود. عند تمكينه، يمكن للمشرف اختيار المجموعات التي يمكنها مشاركة الملفات. يمكن للمستخدمين المُخوَّلِين مشاركة الملفات والمجلدات مع مستخدمين و مجموعات أخرى داخل نكست كلاود. بالإضافة إلى ذلك، إذا قام المشرف بتمكين ميزة المشاركة عبر الروابط، فيمكن استعمال رابط خارجي لمشاركة الملفات مع مستخدمين آخرين خارج نكست كلاود. يمكن للمسؤولين أيضًا فرض كلمات المرور و تواريخ انتهاء الصلاحية و تمكين مشاركة خادوم لخادوم عبر روابط المشاركة. بالإضافة إلى المشاركة من الأجهزة المحمولة.\nيؤدي إيقاف تشغيل الميزة إلى إزالة الملفات والمجلدات المُشارَكة على الخادوم لجميع مستلمي المشاركة، وكذلك على عملاء المزامنة و تطبيقات الأجهزة المحمولة. المزيد من التعليمات، في صفحات توثيق نكست كلاود.",
"Accept user and group shares by default" : "قبول مشاركات المستخدم والمجموعة بشكل افتراضي",
"Read only" : "للقراءة فقط",

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save