diff options
author | John Molakvoæ <skjnldsv@users.noreply.github.com> | 2024-02-24 19:31:22 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-24 19:31:22 +0100 |
commit | b080113fdd81ab28305b0da3e02691e05237f77f (patch) | |
tree | a347e274ff86e40da457c6b580d0e0e5cf97dbe9 /lib | |
parent | c98b0462e33011a99aeaba582d82e52fdb14c3ae (diff) | |
parent | 1d09dec66c0d0962b42587ab3ddadbcb67dcad65 (diff) | |
download | nextcloud-server-b080113fdd81ab28305b0da3e02691e05237f77f.tar.gz nextcloud-server-b080113fdd81ab28305b0da3e02691e05237f77f.zip |
Merge branch 'master' into fix/42480/user-admin-not-admin
Signed-off-by: John Molakvoæ <skjnldsv@users.noreply.github.com>
Diffstat (limited to 'lib')
52 files changed, 1003 insertions, 751 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 9c3bf0f7e9a..e041c3c4d21 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -38,15 +38,18 @@ return array( 'OCP\\AppFramework\\Http' => $baseDir . '/lib/public/AppFramework/Http.php', 'OCP\\AppFramework\\Http\\Attribute\\ARateLimit' => $baseDir . '/lib/public/AppFramework/Http/Attribute/ARateLimit.php', 'OCP\\AppFramework\\Http\\Attribute\\AnonRateLimit' => $baseDir . '/lib/public/AppFramework/Http/Attribute/AnonRateLimit.php', + 'OCP\\AppFramework\\Http\\Attribute\\ApiRoute' => $baseDir . '/lib/public/AppFramework/Http/Attribute/ApiRoute.php', 'OCP\\AppFramework\\Http\\Attribute\\AuthorizedAdminSetting' => $baseDir . '/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php', 'OCP\\AppFramework\\Http\\Attribute\\BruteForceProtection' => $baseDir . '/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php', 'OCP\\AppFramework\\Http\\Attribute\\CORS' => $baseDir . '/lib/public/AppFramework/Http/Attribute/CORS.php', + 'OCP\\AppFramework\\Http\\Attribute\\FrontpageRoute' => $baseDir . '/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php', 'OCP\\AppFramework\\Http\\Attribute\\IgnoreOpenAPI' => $baseDir . '/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php', 'OCP\\AppFramework\\Http\\Attribute\\NoAdminRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php', 'OCP\\AppFramework\\Http\\Attribute\\NoCSRFRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php', 'OCP\\AppFramework\\Http\\Attribute\\OpenAPI' => $baseDir . '/lib/public/AppFramework/Http/Attribute/OpenAPI.php', 'OCP\\AppFramework\\Http\\Attribute\\PasswordConfirmationRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php', 'OCP\\AppFramework\\Http\\Attribute\\PublicPage' => $baseDir . '/lib/public/AppFramework/Http/Attribute/PublicPage.php', + 'OCP\\AppFramework\\Http\\Attribute\\Route' => $baseDir . '/lib/public/AppFramework/Http/Attribute/Route.php', 'OCP\\AppFramework\\Http\\Attribute\\StrictCookiesRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/StrictCookiesRequired.php', 'OCP\\AppFramework\\Http\\Attribute\\SubAdminRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/SubAdminRequired.php', 'OCP\\AppFramework\\Http\\Attribute\\UseSession' => $baseDir . '/lib/public/AppFramework/Http/Attribute/UseSession.php', @@ -426,6 +429,7 @@ return array( 'OCP\\Files\\UnseekableException' => $baseDir . '/lib/public/Files/UnseekableException.php', 'OCP\\Files_FullTextSearch\\Model\\AFilesDocument' => $baseDir . '/lib/public/Files_FullTextSearch/Model/AFilesDocument.php', 'OCP\\FullTextSearch\\Exceptions\\FullTextSearchAppNotAvailableException' => $baseDir . '/lib/public/FullTextSearch/Exceptions/FullTextSearchAppNotAvailableException.php', + 'OCP\\FullTextSearch\\Exceptions\\FullTextSearchIndexNotAvailableException' => $baseDir . '/lib/public/FullTextSearch/Exceptions/FullTextSearchIndexNotAvailableException.php', 'OCP\\FullTextSearch\\IFullTextSearchManager' => $baseDir . '/lib/public/FullTextSearch/IFullTextSearchManager.php', 'OCP\\FullTextSearch\\IFullTextSearchPlatform' => $baseDir . '/lib/public/FullTextSearch/IFullTextSearchPlatform.php', 'OCP\\FullTextSearch\\IFullTextSearchProvider' => $baseDir . '/lib/public/FullTextSearch/IFullTextSearchProvider.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index b222594af75..e75a1d0ec63 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -71,15 +71,18 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\AppFramework\\Http' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http.php', 'OCP\\AppFramework\\Http\\Attribute\\ARateLimit' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/ARateLimit.php', 'OCP\\AppFramework\\Http\\Attribute\\AnonRateLimit' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/AnonRateLimit.php', + 'OCP\\AppFramework\\Http\\Attribute\\ApiRoute' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/ApiRoute.php', 'OCP\\AppFramework\\Http\\Attribute\\AuthorizedAdminSetting' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php', 'OCP\\AppFramework\\Http\\Attribute\\BruteForceProtection' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php', 'OCP\\AppFramework\\Http\\Attribute\\CORS' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/CORS.php', + 'OCP\\AppFramework\\Http\\Attribute\\FrontpageRoute' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php', 'OCP\\AppFramework\\Http\\Attribute\\IgnoreOpenAPI' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php', 'OCP\\AppFramework\\Http\\Attribute\\NoAdminRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php', 'OCP\\AppFramework\\Http\\Attribute\\NoCSRFRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php', 'OCP\\AppFramework\\Http\\Attribute\\OpenAPI' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/OpenAPI.php', 'OCP\\AppFramework\\Http\\Attribute\\PasswordConfirmationRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php', 'OCP\\AppFramework\\Http\\Attribute\\PublicPage' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/PublicPage.php', + 'OCP\\AppFramework\\Http\\Attribute\\Route' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/Route.php', 'OCP\\AppFramework\\Http\\Attribute\\StrictCookiesRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/StrictCookiesRequired.php', 'OCP\\AppFramework\\Http\\Attribute\\SubAdminRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/SubAdminRequired.php', 'OCP\\AppFramework\\Http\\Attribute\\UseSession' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/UseSession.php', @@ -459,6 +462,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Files\\UnseekableException' => __DIR__ . '/../../..' . '/lib/public/Files/UnseekableException.php', 'OCP\\Files_FullTextSearch\\Model\\AFilesDocument' => __DIR__ . '/../../..' . '/lib/public/Files_FullTextSearch/Model/AFilesDocument.php', 'OCP\\FullTextSearch\\Exceptions\\FullTextSearchAppNotAvailableException' => __DIR__ . '/../../..' . '/lib/public/FullTextSearch/Exceptions/FullTextSearchAppNotAvailableException.php', + 'OCP\\FullTextSearch\\Exceptions\\FullTextSearchIndexNotAvailableException' => __DIR__ . '/../../..' . '/lib/public/FullTextSearch/Exceptions/FullTextSearchIndexNotAvailableException.php', 'OCP\\FullTextSearch\\IFullTextSearchManager' => __DIR__ . '/../../..' . '/lib/public/FullTextSearch/IFullTextSearchManager.php', 'OCP\\FullTextSearch\\IFullTextSearchPlatform' => __DIR__ . '/../../..' . '/lib/public/FullTextSearch/IFullTextSearchPlatform.php', 'OCP\\FullTextSearch\\IFullTextSearchProvider' => __DIR__ . '/../../..' . '/lib/public/FullTextSearch/IFullTextSearchProvider.php', diff --git a/lib/l10n/es_MX.js b/lib/l10n/es_MX.js index 56ab97c7bd6..181780c40c9 100644 --- a/lib/l10n/es_MX.js +++ b/lib/l10n/es_MX.js @@ -2,10 +2,19 @@ OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "¡No se puede escribir en el directorio \"config\"!", + "This can usually be fixed by giving the web server write access to the config directory." : "Normalmente, esto se puede arreglar dando al servidor web permiso de escritura al directorio de configuración.", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Sin embargo, si prefiere mantener el archivo config.php como sólo lectura, establezca la opción \"config_is_read_only\" como verdadero.", "See %s" : "Ver %s", + "Application %1$s is not present or has a non-compatible version with this server. Please check the apps directory." : "La aplicación %1$s no está presente o tiene una versión no compatible con este servidor. Por favor, revise el directorio de aplicaciones.", "Sample configuration detected" : "Se ha detectado la configuración de muestra", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Se ha detectado que la configuración de muestra ha sido copiada. Esto puede arruiniar tu instalacón y no está soportado. Por favor lee la documentación antes de hacer cambios en el archivo config.php", "The page could not be found on the server." : "No se pudo encontrar la página en el servidor.", + "%s email verification" : "%s verificación de correo electrónico", + "Email verification" : "Verificación de correo electrónico", + "Click the following button to confirm your email." : "Haga clic en el siguiente botón para confirmar su correo electrónico.", + "Click the following link to confirm your email." : "Haga clic en el siguiente enlace para confirmar su correo electrónico.", + "Confirm your email" : "Confirmar su correo electrónico", + "Other activities" : "Otras actividades", "%1$s and %2$s" : "%1$s y %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s y %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s y %4$s", @@ -13,19 +22,42 @@ OC.L10N.register( "Education Edition" : "Edición Educativa", "Enterprise bundle" : "Paquete empresarial", "Groupware bundle" : "Paquete de Groupware", + "Hub bundle" : "Paquete Hub", "Social sharing bundle" : "Paquete para compartir en redes sociales", "PHP %s or higher is required." : "Se requiere de PHP %s o superior.", "PHP with a version lower than %s is required." : "PHP con una versión inferiror a la %s es requerido. ", "%sbit or higher PHP required." : "se requiere PHP para %sbit o superior.", + "The following architectures are supported: %s" : "Las siguientes arquitecturas están soportadas: %s", + "The following databases are supported: %s" : "Las siguientes bases de datos están soportadas: %s", "The command line tool %s could not be found" : "No fue posible encontar la herramienta de línea de comando %s", "The library %s is not available." : "La biblioteca %s no está disponible. ", + "Library %1$s with a version higher than %2$s is required - available version %3$s." : "Se requiere la librería %1$scon una versión superior a %2$s- versión disponible %3$s.", + "Library %1$s with a version lower than %2$s is required - available version %3$s." : "Se requiere la librería %1$s con una versión inferior a %2$s- versión disponible %3$s.", + "The following platforms are supported: %s" : "Las siguientes plataformas están soportadas: %s", "Server version %s or higher is required." : "Se requiere la versión del servidor %s o superior. ", "Server version %s or lower is required." : "La versión del servidor %s o inferior es requerdia. ", + "Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "El usuario que ha iniciado sesión debe ser un administrador, un subadministrador o tener permisos especiales para acceder a esta configuración", + "Logged in account must be an admin or sub admin" : "El usuario conectado debe ser un administrador o un subadministrador", + "Logged in account must be an admin" : "El usuario conectado debe ser un administrador", + "Wiping of device %s has started" : "La limpieza del dispositivo %s ha comenzado", + "Wiping of device »%s« has started" : "La limpieza del dispositivo »%s« ha comenzado", + "»%s« started remote wipe" : "»%s« ha iniciado la limpieza remota", + "Device or application »%s« has started the remote wipe process. You will receive another email once the process has finished" : "El dispositivo o la aplicación »%s« ha iniciado el proceso de limpieza remoto. Recibirá otro correo electrónico cuando el proceso haya finalizado", + "Wiping of device %s has finished" : "La limpieza del dispositivo %s ha finalizado", + "Wiping of device »%s« has finished" : "La limpieza del dispositivo »%s« ha finalizado", + "»%s« finished remote wipe" : "»%s« ha finalizado la limpieza remota", + "Device or application »%s« has finished the remote wipe process." : "El dispositivo o la aplicación »%s« ha finalizado el proceso de limpieza remoto.", + "Remote wipe started" : "La limpieza remota ha iniciado", + "A remote wipe was started on device %s" : "Se ha iniciado la limpieza remota en el dispositivo %s", + "Remote wipe finished" : "La limpieza remota ha finalizado", + "The remote wipe on %s has finished" : "La limpieza remota en %s ha finalizado", "Authentication" : "Autenticación", "Unknown filetype" : "Tipo de archivo desconocido", "Invalid image" : "Imagen inválida", "Avatar image is not square" : "La imagen del avatar no es un cuadrado", "Files" : "Archivos", + "View profile" : "Ver perfil", + "Local time: %s" : "Hora local: %s", "today" : "hoy", "tomorrow" : "mañana", "yesterday" : "ayer", @@ -45,8 +77,11 @@ OC.L10N.register( "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"], "in a few seconds" : "en algunos segundos", "seconds ago" : "hace segundos", + "Empty file" : "Archivo vacío", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ", "File already exists" : "El archivo ya existe", + "Invalid path" : "Ruta inválida", + "Failed to create file from template" : "No se pudo crear un archivo desde la plantilla", "Templates" : "Plantillas", "File name is a reserved word" : "Nombre de archivo es una palabra reservada", "File name contains at least one invalid character" : "El nombre del archivo contiene al menos un caracter inválido", @@ -58,34 +93,70 @@ OC.L10N.register( "__language_name__" : "Español (México)", "This is an automatically sent email, please do not reply." : "Este es un correo enviado automáticamente, por favor no lo contestes. ", "Help" : "Ayuda", + "Appearance and accessibility" : "Apariencia y accesibilidad", "Apps" : "Aplicaciones", + "Personal settings" : "Configuración personal", + "Administration settings" : "Configuración de administración", "Settings" : "Ajustes", "Log out" : "Cerrar sesión", "Users" : "Usuarios", "Email" : "Correo electrónico", + "Mail %s" : "Correo %s", + "Fediverse" : "Fediverso", + "View %s on the fediverse" : "Ver %s en el fediverso", "Phone" : "Teléfono fijo", + "Call %s" : "Llamar a %s", "Twitter" : "Twitter", + "View %s on Twitter" : "Ver %s en Twitter", "Website" : "Sitio web", + "Visit %s" : "Visitar %s", "Address" : "Dirección", "Profile picture" : "Foto de perfil", "About" : "Acerca de", + "Display name" : "Nombre para mostrar", + "Headline" : "Título", + "Organisation" : "Organización", + "Role" : "Cargo", + "Unknown account" : "Cuenta desconocida", "Additional settings" : "Configuraciones adicionales", + "Enter the database Login and name for %s" : "Introduzca el usuario y el nombre para la base de datos %s", + "Enter the database Login for %s" : "Introduzca el usuario de la base de datos %s", + "Enter the database name for %s" : "Introduzca el nombre de la base de datos %s", + "You cannot use dots in the database name %s" : "No puede utilizar puntos para el nombre de base de datos %s ", + "MySQL Login and/or password not valid" : "Usuario y/o contraseña de MySQL inválidos", "You need to enter details of an existing account." : "Necesitas ingresar los detalles de una cuenta existente.", "Oracle connection could not be established" : "No fue posible establecer la conexión a Oracle", + "Oracle Login and/or password not valid" : "Usuario y/o contraseña de Oracle inválidos", + "PostgreSQL Login and/or password not valid" : "Usuario y/o contraseña de PostgreSQL inválidos", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "OS X de Mac no está soportado y %s no funcionará correctamente en esta plataforma ¡Úsalo bajo tu propio riesgo!", "For the best results, please consider using a GNU/Linux server instead." : "Para mejores resultados, por favor cosidera usar en su lugar un servidor GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Al parecer esta instancia %s está corriendo en un ambiente PHP de 32-bits y el open_basedir ha sido configurado en el archivo php.ini. Esto generará problemas con archivos de más de 4GB de tamaño y es altamente desalentado. ", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Por favor elimina el ajuste open_basedir de tu archivo php.ini o cambia a PHP de 64 bits. ", + "Set an admin Login." : "Establecer un usuario administrador.", "Set an admin password." : "Establecer la contraseña del administrador.", + "Cannot create or write into the data directory %s" : "No se puede crear o escribir en el directorio de datos %s", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "El backend %s que comparte debe implementar la interface OCP\\Share_Backend", "Sharing backend %s not found" : "No fue encontrado el Backend que comparte %s ", "Sharing backend for %s not found" : "No fue encontrado el Backend que comparte para %s", + "%1$s shared »%2$s« with you and wants to add:" : "%1$s compartió »%2$s« contigo y quiere añadir:", + "%1$s shared »%2$s« with you and wants to add" : "%1$s compartió »%2$s« contigo y quiere añadir", + "»%s« added a note to a file shared with you" : "»%s« añadió una nota a un archivo compartido contigo", "Open »%s«" : "Abrir »%s«", + "%1$s via %2$s" : "%1$s vía %2$s", "You are not allowed to share %s" : "No tienes permitido compartir %s", "Cannot increase permissions of %s" : "No se pueden incrementar los permisos de %s", + "Files cannot be shared with delete permissions" : "No se pueden compartir archivos con permisos de eliminación", + "Files cannot be shared with create permissions" : "No se pueden compartir archivos con permisos de creación", "Expiration date is in the past" : "La fecha de expiración se encuentra en el pasado", + "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["No se puede fijar la fecha de caducidad más de %n día en el futuro","No se puede fijar la fecha de caducidad más de %n días en el futuro","No se puede fijar la fecha de caducidad más de %n días en el futuro"], + "Sharing is only allowed with group members" : "Sólo está permitido compartir a los miembros del grupo", + "Sharing %s failed, because this item is already shared with the account %s" : "No se pudo compartir %s porque este elemento ya está compartido con el usuario %s", + "%1$s shared »%2$s« with you" : "%1$s compartió »%2$s« contigo", + "%1$s shared »%2$s« with you." : "%1$s compartió »%2$s« contigo.", "Click the button below to open it." : "Haz click en el botón inferior para abrirlo. ", "The requested share does not exist anymore" : "El recurso compartido solicitado ya no existe", + "The requested share comes from a disabled user" : "El recurso compartido solicitado proviene de un usuario deshabilitado", + "The user was not created because the user limit has been reached. Check your notifications to learn more." : "No se creó el usuario porque se ha alcanzado el límite de usuarios. Consulta tus notificaciones para obtener más información.", "Could not find category \"%s\"" : "No fue posible encontrar la categoria \"%s\"", "Sunday" : "Domingo", "Monday" : "Lunes", @@ -133,28 +204,72 @@ OC.L10N.register( "Nov." : "Nov.", "Dec." : "Dic.", "A valid password must be provided" : "Se debe proporcionar una contraseña válida", + "The Login is already being used" : "El usuario ya está en uso", + "Could not create account" : "No se pudo crear la cuenta", + "Only the following characters are allowed in an Login: \"a-z\", \"A-Z\", \"0-9\", spaces and \"_.@-'\"" : "Sólo los siguientes caracteres están permitidos en un nombre de usuario: \"a-z\", \"A-Z\", \"0-9\", espacios y \"_.@-'\"", + "A valid Login must be provided" : "Se debe proporcionar un usuario válido", + "Login contains whitespace at the beginning or at the end" : "El usuario contiene espacios en blanco al inicio o al final", + "Login must not consist of dots only" : "El usuario no debe consistir sólo de puntos", + "Login is invalid because files already exist for this user" : "El usuario es inválido porque ya existen archivos para éste", + "Account disabled" : "Cuenta deshabilitada", "Login canceled by app" : "Inicio de sesión cancelado por la aplicación", + "App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "No se puede instalar la aplicación \"%1$s\" porque no se cumple con las siguientes dependencias: %2$s ", "a safe home for all your data" : "un lugar seguro para todos tus datos", "File is currently busy, please try again later" : "El archivo se encuentra actualmente en uso, por favor intentalo más tarde. ", + "Cannot download file" : "No se puede descargar el archivo", "Application is not enabled" : "La aplicación está deshabilitada", "Authentication error" : "Error de autenticación", "Token expired. Please reload page." : "La ficha ha expirado. Por favor recarga la página.", "No database drivers (sqlite, mysql, or postgresql) installed." : "No cuentas con controladores de base de datos (sqlite, mysql o postgresql) instalados. ", + "Cannot write into \"config\" directory." : "No se puede escribir en el directorio \"config\".", + "This can usually be fixed by giving the web server write access to the config directory. See %s" : "Normalmente, esto se puede arreglar al darle al servidor web permiso de escritura al directorio de configuración. Vea %s", + "Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "Sin embargo, si prefiere mantener el archivo config.php como sólo lectura, establezca la opción \"config_is_read_only\" como verdadero. Vea %s", + "Cannot write into \"apps\" directory." : "No se puede escribir en el directorio \"apps\".", + "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Normalmente, esto se puede arreglar al darle al servidor web permiso de escritura al directorio de aplicaciones o deshabilitando la tienda de aplicaciones del archivo de configuración.", + "Cannot create \"data\" directory." : "No se puede crear el directorio \"data\".", + "This can usually be fixed by giving the web server write access to the root directory. See %s" : "Normalmente, esto se puede arreglar dando al servidor web permiso de escritura al directorio raíz. Vea %s", + "Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "Los permisos normalmente se pueden arreglar dando al servidor web permiso de escritura al directorio raíz. Vea %s.", + "Your data directory is not writable." : "No se puede escribir en su carpeta de datos.", + "Setting locale to %s failed." : "Falló al establecer la configuración regional a %s.", + "Please install one of these locales on your system and restart your web server." : "Por favor, instale una de estas configuraciones regionales en su sistema y reinicie el servidor web.", "PHP module %s not installed." : "El módulo de PHP %s no está instalado. ", "Please ask your server administrator to install the module." : "Por favor solicita a tu adminsitrador la instalación del módulo. ", "PHP setting \"%s\" is not set to \"%s\"." : "El ajuste PHP \"%s\" no esta establecido a \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "El cambiar este ajuste del archivo php.ini hará que Nextcloud corra de nuevo.", + "<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>." : "<code>mbstring.func_overload</code> está configurado como <code>%s</code> en lugar del valor esperado <code>0</code>.", + "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Para arreglar este problema, establezca <code>mbstring.func_overload</code> como<code>0</code> en su php.ini.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Al parecer PHP está configurado para quitar los bloques de comentarios internos. Esto hará que varias aplicaciones principales sean inaccesibles. ", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Esto ha sido causado probablemente por un acelerador de caché como Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "¿Los módulos de PHP han sido instalados, pero se siguen enlistando como faltantes?", "Please ask your server administrator to restart the web server." : "Por favor solicita al administrador reiniciar el servidor web. ", + "The required %s config variable is not configured in the config.php file." : "La variable de configuración requerida %s no está configurada en el archivo config.php.", + "Please ask your server administrator to check the Nextcloud configuration." : "Por favor, pida al administrador del servidor que verifique la configuración de Nextcloud.", + "Your data directory is readable by other people." : "Su directorio de datos puede ser leído por otros usuarios.", + "Please change the permissions to 0770 so that the directory cannot be listed by other people." : "Por favor, cambie los permisos a 0770 para que el directorio no se pueda listar por otros usuarios.", + "Your data directory must be an absolute path." : "Su directorio de datos debe ser una ruta absoluta.", + "Check the value of \"datadirectory\" in your configuration." : "Revise el valor de \"datadirectory\" en su configuración.", + "Your data directory is invalid." : "Su directorio de datos es inválido.", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Asegurate de que exista una archivo llamado \".ocdata\" en la raíz del directorio de datos. ", + "Action \"%s\" not supported or implemented." : "La acción \"%s\" no está soportada o no está implementada.", + "Authentication failed, wrong token or provider ID given" : "Falló la autentificación, se proporcionó un token o un identificador de proveedor incorrecto", + "Parameters missing in order to complete the request. Missing Parameters: \"%s\"" : "Faltan parámetros para completar la solicitud. Parámetros faltantes: \"%s\"", + "ID \"%1$s\" already used by cloud federation provider \"%2$s\"" : "El identificador \"%1$s\" ya está en uso por el proveedor de federación de la nube \"%2$s\"", + "Cloud Federation Provider with ID: \"%s\" does not exist." : "El proveedor de federación de la nube con identificador \"%s\" no existe.", "Could not obtain lock type %d on \"%s\"." : "No fue posible obtener el tipo de bloqueo %d en \"%s\". ", "Storage unauthorized. %s" : "Almacenamiento no autorizado. %s", "Storage incomplete configuration. %s" : "Configuración incompleta del almacenamiento. %s", "Storage connection error. %s" : "Se presentó un error con la conexión al almacenamiento. %s", "Storage is temporarily not available" : "El almacenamieto se encuentra temporalmente no disponible", "Storage connection timeout. %s" : "El tiempo de la conexión del almacenamiento se agotó. %s", + "Free prompt" : "Liberar prompt", + "Runs an arbitrary prompt through the language model." : "Ejecuta un prompt arbitrario a través del modelo de lenguaje.", + "Generate headline" : "Generar titular", + "Generates a possible headline for a text." : "Genera un posible titular para un texto.", + "Summarize" : "Resumir", + "Summarizes text by reducing its length without losing key information." : "Resume el texto reduciendo su longitud sin perder información clave.", + "Extract topics" : "Extraer temas", + "Extracts topics from a text and outputs them separated by commas." : "Extrae los temas de un texto y genera una salida separada por comas. ", + "404" : "404", "Logged in user must be an admin" : "El usuario firmado debe ser un administrador", "Full name" : "Nombre completo", "Unknown user" : "Ususario desconocido", diff --git a/lib/l10n/es_MX.json b/lib/l10n/es_MX.json index 940b0706cfc..c322f723c3c 100644 --- a/lib/l10n/es_MX.json +++ b/lib/l10n/es_MX.json @@ -1,9 +1,18 @@ { "translations": { "Cannot write into \"config\" directory!" : "¡No se puede escribir en el directorio \"config\"!", + "This can usually be fixed by giving the web server write access to the config directory." : "Normalmente, esto se puede arreglar dando al servidor web permiso de escritura al directorio de configuración.", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Sin embargo, si prefiere mantener el archivo config.php como sólo lectura, establezca la opción \"config_is_read_only\" como verdadero.", "See %s" : "Ver %s", + "Application %1$s is not present or has a non-compatible version with this server. Please check the apps directory." : "La aplicación %1$s no está presente o tiene una versión no compatible con este servidor. Por favor, revise el directorio de aplicaciones.", "Sample configuration detected" : "Se ha detectado la configuración de muestra", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Se ha detectado que la configuración de muestra ha sido copiada. Esto puede arruiniar tu instalacón y no está soportado. Por favor lee la documentación antes de hacer cambios en el archivo config.php", "The page could not be found on the server." : "No se pudo encontrar la página en el servidor.", + "%s email verification" : "%s verificación de correo electrónico", + "Email verification" : "Verificación de correo electrónico", + "Click the following button to confirm your email." : "Haga clic en el siguiente botón para confirmar su correo electrónico.", + "Click the following link to confirm your email." : "Haga clic en el siguiente enlace para confirmar su correo electrónico.", + "Confirm your email" : "Confirmar su correo electrónico", + "Other activities" : "Otras actividades", "%1$s and %2$s" : "%1$s y %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s y %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s y %4$s", @@ -11,19 +20,42 @@ "Education Edition" : "Edición Educativa", "Enterprise bundle" : "Paquete empresarial", "Groupware bundle" : "Paquete de Groupware", + "Hub bundle" : "Paquete Hub", "Social sharing bundle" : "Paquete para compartir en redes sociales", "PHP %s or higher is required." : "Se requiere de PHP %s o superior.", "PHP with a version lower than %s is required." : "PHP con una versión inferiror a la %s es requerido. ", "%sbit or higher PHP required." : "se requiere PHP para %sbit o superior.", + "The following architectures are supported: %s" : "Las siguientes arquitecturas están soportadas: %s", + "The following databases are supported: %s" : "Las siguientes bases de datos están soportadas: %s", "The command line tool %s could not be found" : "No fue posible encontar la herramienta de línea de comando %s", "The library %s is not available." : "La biblioteca %s no está disponible. ", + "Library %1$s with a version higher than %2$s is required - available version %3$s." : "Se requiere la librería %1$scon una versión superior a %2$s- versión disponible %3$s.", + "Library %1$s with a version lower than %2$s is required - available version %3$s." : "Se requiere la librería %1$s con una versión inferior a %2$s- versión disponible %3$s.", + "The following platforms are supported: %s" : "Las siguientes plataformas están soportadas: %s", "Server version %s or higher is required." : "Se requiere la versión del servidor %s o superior. ", "Server version %s or lower is required." : "La versión del servidor %s o inferior es requerdia. ", + "Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "El usuario que ha iniciado sesión debe ser un administrador, un subadministrador o tener permisos especiales para acceder a esta configuración", + "Logged in account must be an admin or sub admin" : "El usuario conectado debe ser un administrador o un subadministrador", + "Logged in account must be an admin" : "El usuario conectado debe ser un administrador", + "Wiping of device %s has started" : "La limpieza del dispositivo %s ha comenzado", + "Wiping of device »%s« has started" : "La limpieza del dispositivo »%s« ha comenzado", + "»%s« started remote wipe" : "»%s« ha iniciado la limpieza remota", + "Device or application »%s« has started the remote wipe process. You will receive another email once the process has finished" : "El dispositivo o la aplicación »%s« ha iniciado el proceso de limpieza remoto. Recibirá otro correo electrónico cuando el proceso haya finalizado", + "Wiping of device %s has finished" : "La limpieza del dispositivo %s ha finalizado", + "Wiping of device »%s« has finished" : "La limpieza del dispositivo »%s« ha finalizado", + "»%s« finished remote wipe" : "»%s« ha finalizado la limpieza remota", + "Device or application »%s« has finished the remote wipe process." : "El dispositivo o la aplicación »%s« ha finalizado el proceso de limpieza remoto.", + "Remote wipe started" : "La limpieza remota ha iniciado", + "A remote wipe was started on device %s" : "Se ha iniciado la limpieza remota en el dispositivo %s", + "Remote wipe finished" : "La limpieza remota ha finalizado", + "The remote wipe on %s has finished" : "La limpieza remota en %s ha finalizado", "Authentication" : "Autenticación", "Unknown filetype" : "Tipo de archivo desconocido", "Invalid image" : "Imagen inválida", "Avatar image is not square" : "La imagen del avatar no es un cuadrado", "Files" : "Archivos", + "View profile" : "Ver perfil", + "Local time: %s" : "Hora local: %s", "today" : "hoy", "tomorrow" : "mañana", "yesterday" : "ayer", @@ -43,8 +75,11 @@ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"], "in a few seconds" : "en algunos segundos", "seconds ago" : "hace segundos", + "Empty file" : "Archivo vacío", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ", "File already exists" : "El archivo ya existe", + "Invalid path" : "Ruta inválida", + "Failed to create file from template" : "No se pudo crear un archivo desde la plantilla", "Templates" : "Plantillas", "File name is a reserved word" : "Nombre de archivo es una palabra reservada", "File name contains at least one invalid character" : "El nombre del archivo contiene al menos un caracter inválido", @@ -56,34 +91,70 @@ "__language_name__" : "Español (México)", "This is an automatically sent email, please do not reply." : "Este es un correo enviado automáticamente, por favor no lo contestes. ", "Help" : "Ayuda", + "Appearance and accessibility" : "Apariencia y accesibilidad", "Apps" : "Aplicaciones", + "Personal settings" : "Configuración personal", + "Administration settings" : "Configuración de administración", "Settings" : "Ajustes", "Log out" : "Cerrar sesión", "Users" : "Usuarios", "Email" : "Correo electrónico", + "Mail %s" : "Correo %s", + "Fediverse" : "Fediverso", + "View %s on the fediverse" : "Ver %s en el fediverso", "Phone" : "Teléfono fijo", + "Call %s" : "Llamar a %s", "Twitter" : "Twitter", + "View %s on Twitter" : "Ver %s en Twitter", "Website" : "Sitio web", + "Visit %s" : "Visitar %s", "Address" : "Dirección", "Profile picture" : "Foto de perfil", "About" : "Acerca de", + "Display name" : "Nombre para mostrar", + "Headline" : "Título", + "Organisation" : "Organización", + "Role" : "Cargo", + "Unknown account" : "Cuenta desconocida", "Additional settings" : "Configuraciones adicionales", + "Enter the database Login and name for %s" : "Introduzca el usuario y el nombre para la base de datos %s", + "Enter the database Login for %s" : "Introduzca el usuario de la base de datos %s", + "Enter the database name for %s" : "Introduzca el nombre de la base de datos %s", + "You cannot use dots in the database name %s" : "No puede utilizar puntos para el nombre de base de datos %s ", + "MySQL Login and/or password not valid" : "Usuario y/o contraseña de MySQL inválidos", "You need to enter details of an existing account." : "Necesitas ingresar los detalles de una cuenta existente.", "Oracle connection could not be established" : "No fue posible establecer la conexión a Oracle", + "Oracle Login and/or password not valid" : "Usuario y/o contraseña de Oracle inválidos", + "PostgreSQL Login and/or password not valid" : "Usuario y/o contraseña de PostgreSQL inválidos", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "OS X de Mac no está soportado y %s no funcionará correctamente en esta plataforma ¡Úsalo bajo tu propio riesgo!", "For the best results, please consider using a GNU/Linux server instead." : "Para mejores resultados, por favor cosidera usar en su lugar un servidor GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Al parecer esta instancia %s está corriendo en un ambiente PHP de 32-bits y el open_basedir ha sido configurado en el archivo php.ini. Esto generará problemas con archivos de más de 4GB de tamaño y es altamente desalentado. ", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Por favor elimina el ajuste open_basedir de tu archivo php.ini o cambia a PHP de 64 bits. ", + "Set an admin Login." : "Establecer un usuario administrador.", "Set an admin password." : "Establecer la contraseña del administrador.", + "Cannot create or write into the data directory %s" : "No se puede crear o escribir en el directorio de datos %s", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "El backend %s que comparte debe implementar la interface OCP\\Share_Backend", "Sharing backend %s not found" : "No fue encontrado el Backend que comparte %s ", "Sharing backend for %s not found" : "No fue encontrado el Backend que comparte para %s", + "%1$s shared »%2$s« with you and wants to add:" : "%1$s compartió »%2$s« contigo y quiere añadir:", + "%1$s shared »%2$s« with you and wants to add" : "%1$s compartió »%2$s« contigo y quiere añadir", + "»%s« added a note to a file shared with you" : "»%s« añadió una nota a un archivo compartido contigo", "Open »%s«" : "Abrir »%s«", + "%1$s via %2$s" : "%1$s vía %2$s", "You are not allowed to share %s" : "No tienes permitido compartir %s", "Cannot increase permissions of %s" : "No se pueden incrementar los permisos de %s", + "Files cannot be shared with delete permissions" : "No se pueden compartir archivos con permisos de eliminación", + "Files cannot be shared with create permissions" : "No se pueden compartir archivos con permisos de creación", "Expiration date is in the past" : "La fecha de expiración se encuentra en el pasado", + "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["No se puede fijar la fecha de caducidad más de %n día en el futuro","No se puede fijar la fecha de caducidad más de %n días en el futuro","No se puede fijar la fecha de caducidad más de %n días en el futuro"], + "Sharing is only allowed with group members" : "Sólo está permitido compartir a los miembros del grupo", + "Sharing %s failed, because this item is already shared with the account %s" : "No se pudo compartir %s porque este elemento ya está compartido con el usuario %s", + "%1$s shared »%2$s« with you" : "%1$s compartió »%2$s« contigo", + "%1$s shared »%2$s« with you." : "%1$s compartió »%2$s« contigo.", "Click the button below to open it." : "Haz click en el botón inferior para abrirlo. ", "The requested share does not exist anymore" : "El recurso compartido solicitado ya no existe", + "The requested share comes from a disabled user" : "El recurso compartido solicitado proviene de un usuario deshabilitado", + "The user was not created because the user limit has been reached. Check your notifications to learn more." : "No se creó el usuario porque se ha alcanzado el límite de usuarios. Consulta tus notificaciones para obtener más información.", "Could not find category \"%s\"" : "No fue posible encontrar la categoria \"%s\"", "Sunday" : "Domingo", "Monday" : "Lunes", @@ -131,28 +202,72 @@ "Nov." : "Nov.", "Dec." : "Dic.", "A valid password must be provided" : "Se debe proporcionar una contraseña válida", + "The Login is already being used" : "El usuario ya está en uso", + "Could not create account" : "No se pudo crear la cuenta", + "Only the following characters are allowed in an Login: \"a-z\", \"A-Z\", \"0-9\", spaces and \"_.@-'\"" : "Sólo los siguientes caracteres están permitidos en un nombre de usuario: \"a-z\", \"A-Z\", \"0-9\", espacios y \"_.@-'\"", + "A valid Login must be provided" : "Se debe proporcionar un usuario válido", + "Login contains whitespace at the beginning or at the end" : "El usuario contiene espacios en blanco al inicio o al final", + "Login must not consist of dots only" : "El usuario no debe consistir sólo de puntos", + "Login is invalid because files already exist for this user" : "El usuario es inválido porque ya existen archivos para éste", + "Account disabled" : "Cuenta deshabilitada", "Login canceled by app" : "Inicio de sesión cancelado por la aplicación", + "App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "No se puede instalar la aplicación \"%1$s\" porque no se cumple con las siguientes dependencias: %2$s ", "a safe home for all your data" : "un lugar seguro para todos tus datos", "File is currently busy, please try again later" : "El archivo se encuentra actualmente en uso, por favor intentalo más tarde. ", + "Cannot download file" : "No se puede descargar el archivo", "Application is not enabled" : "La aplicación está deshabilitada", "Authentication error" : "Error de autenticación", "Token expired. Please reload page." : "La ficha ha expirado. Por favor recarga la página.", "No database drivers (sqlite, mysql, or postgresql) installed." : "No cuentas con controladores de base de datos (sqlite, mysql o postgresql) instalados. ", + "Cannot write into \"config\" directory." : "No se puede escribir en el directorio \"config\".", + "This can usually be fixed by giving the web server write access to the config directory. See %s" : "Normalmente, esto se puede arreglar al darle al servidor web permiso de escritura al directorio de configuración. Vea %s", + "Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "Sin embargo, si prefiere mantener el archivo config.php como sólo lectura, establezca la opción \"config_is_read_only\" como verdadero. Vea %s", + "Cannot write into \"apps\" directory." : "No se puede escribir en el directorio \"apps\".", + "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Normalmente, esto se puede arreglar al darle al servidor web permiso de escritura al directorio de aplicaciones o deshabilitando la tienda de aplicaciones del archivo de configuración.", + "Cannot create \"data\" directory." : "No se puede crear el directorio \"data\".", + "This can usually be fixed by giving the web server write access to the root directory. See %s" : "Normalmente, esto se puede arreglar dando al servidor web permiso de escritura al directorio raíz. Vea %s", + "Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "Los permisos normalmente se pueden arreglar dando al servidor web permiso de escritura al directorio raíz. Vea %s.", + "Your data directory is not writable." : "No se puede escribir en su carpeta de datos.", + "Setting locale to %s failed." : "Falló al establecer la configuración regional a %s.", + "Please install one of these locales on your system and restart your web server." : "Por favor, instale una de estas configuraciones regionales en su sistema y reinicie el servidor web.", "PHP module %s not installed." : "El módulo de PHP %s no está instalado. ", "Please ask your server administrator to install the module." : "Por favor solicita a tu adminsitrador la instalación del módulo. ", "PHP setting \"%s\" is not set to \"%s\"." : "El ajuste PHP \"%s\" no esta establecido a \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "El cambiar este ajuste del archivo php.ini hará que Nextcloud corra de nuevo.", + "<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>." : "<code>mbstring.func_overload</code> está configurado como <code>%s</code> en lugar del valor esperado <code>0</code>.", + "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Para arreglar este problema, establezca <code>mbstring.func_overload</code> como<code>0</code> en su php.ini.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Al parecer PHP está configurado para quitar los bloques de comentarios internos. Esto hará que varias aplicaciones principales sean inaccesibles. ", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Esto ha sido causado probablemente por un acelerador de caché como Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "¿Los módulos de PHP han sido instalados, pero se siguen enlistando como faltantes?", "Please ask your server administrator to restart the web server." : "Por favor solicita al administrador reiniciar el servidor web. ", + "The required %s config variable is not configured in the config.php file." : "La variable de configuración requerida %s no está configurada en el archivo config.php.", + "Please ask your server administrator to check the Nextcloud configuration." : "Por favor, pida al administrador del servidor que verifique la configuración de Nextcloud.", + "Your data directory is readable by other people." : "Su directorio de datos puede ser leído por otros usuarios.", + "Please change the permissions to 0770 so that the directory cannot be listed by other people." : "Por favor, cambie los permisos a 0770 para que el directorio no se pueda listar por otros usuarios.", + "Your data directory must be an absolute path." : "Su directorio de datos debe ser una ruta absoluta.", + "Check the value of \"datadirectory\" in your configuration." : "Revise el valor de \"datadirectory\" en su configuración.", + "Your data directory is invalid." : "Su directorio de datos es inválido.", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Asegurate de que exista una archivo llamado \".ocdata\" en la raíz del directorio de datos. ", + "Action \"%s\" not supported or implemented." : "La acción \"%s\" no está soportada o no está implementada.", + "Authentication failed, wrong token or provider ID given" : "Falló la autentificación, se proporcionó un token o un identificador de proveedor incorrecto", + "Parameters missing in order to complete the request. Missing Parameters: \"%s\"" : "Faltan parámetros para completar la solicitud. Parámetros faltantes: \"%s\"", + "ID \"%1$s\" already used by cloud federation provider \"%2$s\"" : "El identificador \"%1$s\" ya está en uso por el proveedor de federación de la nube \"%2$s\"", + "Cloud Federation Provider with ID: \"%s\" does not exist." : "El proveedor de federación de la nube con identificador \"%s\" no existe.", "Could not obtain lock type %d on \"%s\"." : "No fue posible obtener el tipo de bloqueo %d en \"%s\". ", "Storage unauthorized. %s" : "Almacenamiento no autorizado. %s", "Storage incomplete configuration. %s" : "Configuración incompleta del almacenamiento. %s", "Storage connection error. %s" : "Se presentó un error con la conexión al almacenamiento. %s", "Storage is temporarily not available" : "El almacenamieto se encuentra temporalmente no disponible", "Storage connection timeout. %s" : "El tiempo de la conexión del almacenamiento se agotó. %s", + "Free prompt" : "Liberar prompt", + "Runs an arbitrary prompt through the language model." : "Ejecuta un prompt arbitrario a través del modelo de lenguaje.", + "Generate headline" : "Generar titular", + "Generates a possible headline for a text." : "Genera un posible titular para un texto.", + "Summarize" : "Resumir", + "Summarizes text by reducing its length without losing key information." : "Resume el texto reduciendo su longitud sin perder información clave.", + "Extract topics" : "Extraer temas", + "Extracts topics from a text and outputs them separated by commas." : "Extrae los temas de un texto y genera una salida separada por comas. ", + "404" : "404", "Logged in user must be an admin" : "El usuario firmado debe ser un administrador", "Full name" : "Nombre completo", "Unknown user" : "Ususario desconocido", diff --git a/lib/l10n/he.js b/lib/l10n/he.js index 397f81cf364..489871b7f75 100644 --- a/lib/l10n/he.js +++ b/lib/l10n/he.js @@ -33,14 +33,14 @@ OC.L10N.register( "_%n day ago_::_%n days ago_" : ["לפני %n יום","לפני %n ימים","לפני %n ימים","לפני %n ימים"], "next month" : "בחודש הבא", "last month" : "חודש שעבר", - "_in %n month_::_in %n months_" : ["בעוד חודש","בעוד חודשיים","בעוד %n חודשים","בעוד %n חודשים"], - "_%n month ago_::_%n months ago_" : ["לפני חודש","לפני חודשיים","לפני %n חודשים","לפני %n חודשים"], + "_in %n month_::_in %n months_" : ["בעוד חודש","בעוד חודשיים","בעוד %n חודשים"], + "_%n month ago_::_%n months ago_" : ["לפני חודש","לפני חודשיים","לפני %n חודשים"], "next year" : "בשנה הבאה", "last year" : "שנה שעברה", "_in %n year_::_in %n years_" : ["בעוד שנה","בעוד שנתיים","בעוד %n שנים","בעוד %n שנים"], "_%n year ago_::_%n years ago_" : ["לפני %n שנה","לפני %n שנים","לפני %n שנים","לפני %n שנים"], "_in %n hour_::_in %n hours_" : ["בעוד שעה","בעוד שעתיים","בעוד %n שעות","בעוד %n שעות"], - "_%n hour ago_::_%n hours ago_" : ["לפני שעה","לפני שעתיים","לפני %n שעות","לפני %n שעות"], + "_%n hour ago_::_%n hours ago_" : ["לפני שעה","לפני שעתיים","לפני %n שעות"], "_in %n minute_::_in %n minutes_" : ["בעוד דקה","בעוד 2 דקות","בעוד %n דקות","בעוד %n דקות"], "_%n minute ago_::_%n minutes ago_" : ["לפני דקה","לפני 2 דקות","לפני %n דקות","לפני %n דקות"], "in a few seconds" : "בעוד מספר שניות", @@ -185,4 +185,4 @@ OC.L10N.register( "To fix this issue update your libxml2 version and restart your web server." : "לתיקון הבעיה יש לעדכן את גרסת ה- libxml2 שלך ולהפעיל מחדש את שרת האינטרנט שלך.", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "יש לשנות את ההרשאות ל- 0770 כך שהתיקייה לא תרשם על ידי משתמשים אחרים." }, -"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;"); +"nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;"); diff --git a/lib/l10n/he.json b/lib/l10n/he.json index 34402bc6c94..4f583641a36 100644 --- a/lib/l10n/he.json +++ b/lib/l10n/he.json @@ -31,14 +31,14 @@ "_%n day ago_::_%n days ago_" : ["לפני %n יום","לפני %n ימים","לפני %n ימים","לפני %n ימים"], "next month" : "בחודש הבא", "last month" : "חודש שעבר", - "_in %n month_::_in %n months_" : ["בעוד חודש","בעוד חודשיים","בעוד %n חודשים","בעוד %n חודשים"], - "_%n month ago_::_%n months ago_" : ["לפני חודש","לפני חודשיים","לפני %n חודשים","לפני %n חודשים"], + "_in %n month_::_in %n months_" : ["בעוד חודש","בעוד חודשיים","בעוד %n חודשים"], + "_%n month ago_::_%n months ago_" : ["לפני חודש","לפני חודשיים","לפני %n חודשים"], "next year" : "בשנה הבאה", "last year" : "שנה שעברה", "_in %n year_::_in %n years_" : ["בעוד שנה","בעוד שנתיים","בעוד %n שנים","בעוד %n שנים"], "_%n year ago_::_%n years ago_" : ["לפני %n שנה","לפני %n שנים","לפני %n שנים","לפני %n שנים"], "_in %n hour_::_in %n hours_" : ["בעוד שעה","בעוד שעתיים","בעוד %n שעות","בעוד %n שעות"], - "_%n hour ago_::_%n hours ago_" : ["לפני שעה","לפני שעתיים","לפני %n שעות","לפני %n שעות"], + "_%n hour ago_::_%n hours ago_" : ["לפני שעה","לפני שעתיים","לפני %n שעות"], "_in %n minute_::_in %n minutes_" : ["בעוד דקה","בעוד 2 דקות","בעוד %n דקות","בעוד %n דקות"], "_%n minute ago_::_%n minutes ago_" : ["לפני דקה","לפני 2 דקות","לפני %n דקות","לפני %n דקות"], "in a few seconds" : "בעוד מספר שניות", @@ -182,5 +182,5 @@ "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 נדרש לכל הפחות. כרגע %s מותקן.", "To fix this issue update your libxml2 version and restart your web server." : "לתיקון הבעיה יש לעדכן את גרסת ה- libxml2 שלך ולהפעיל מחדש את שרת האינטרנט שלך.", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "יש לשנות את ההרשאות ל- 0770 כך שהתיקייה לא תרשם על ידי משתמשים אחרים." -},"pluralForm" :"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;" +},"pluralForm" :"nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;" }
\ No newline at end of file diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index 60e55a314d6..5eeb7ef1fbd 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -74,14 +74,6 @@ class AppManager implements IAppManager { 'prevent_group_restriction', ]; - private IUserSession $userSession; - private IConfig $config; - private AppConfig $appConfig; - private IGroupManager $groupManager; - private ICacheFactory $memCacheFactory; - private IEventDispatcher $dispatcher; - private LoggerInterface $logger; - /** @var string[] $appId => $enabled */ private array $installedAppsCache = []; @@ -104,20 +96,15 @@ class AppManager implements IAppManager { /** @var array<string, true> */ private array $loadedApps = []; - public function __construct(IUserSession $userSession, - IConfig $config, - AppConfig $appConfig, - IGroupManager $groupManager, - ICacheFactory $memCacheFactory, - IEventDispatcher $dispatcher, - LoggerInterface $logger) { - $this->userSession = $userSession; - $this->config = $config; - $this->appConfig = $appConfig; - $this->groupManager = $groupManager; - $this->memCacheFactory = $memCacheFactory; - $this->dispatcher = $dispatcher; - $this->logger = $logger; + public function __construct( + private IUserSession $userSession, + private IConfig $config, + private AppConfig $appConfig, + private IGroupManager $groupManager, + private ICacheFactory $memCacheFactory, + private IEventDispatcher $dispatcher, + private LoggerInterface $logger, + ) { } /** diff --git a/lib/private/App/AppStore/Bundles/Bundle.php b/lib/private/App/AppStore/Bundles/Bundle.php index dfc93fdfaa2..fb86edff559 100644 --- a/lib/private/App/AppStore/Bundles/Bundle.php +++ b/lib/private/App/AppStore/Bundles/Bundle.php @@ -26,14 +26,12 @@ namespace OC\App\AppStore\Bundles; use OCP\IL10N; abstract class Bundle { - /** @var IL10N */ - protected $l10n; - /** * @param IL10N $l10n */ - public function __construct(IL10N $l10n) { - $this->l10n = $l10n; + public function __construct( + protected IL10N $l10n, + ) { } /** diff --git a/lib/private/App/AppStore/Bundles/BundleFetcher.php b/lib/private/App/AppStore/Bundles/BundleFetcher.php index 0d2bb61495f..7ac70591e8a 100644 --- a/lib/private/App/AppStore/Bundles/BundleFetcher.php +++ b/lib/private/App/AppStore/Bundles/BundleFetcher.php @@ -27,10 +27,9 @@ namespace OC\App\AppStore\Bundles; use OCP\IL10N; class BundleFetcher { - private IL10N $l10n; - - public function __construct(IL10N $l10n) { - $this->l10n = $l10n; + public function __construct( + private IL10N $l10n, + ) { } /** diff --git a/lib/private/App/AppStore/Fetcher/AppFetcher.php b/lib/private/App/AppStore/Fetcher/AppFetcher.php index f9fbd05855b..bb1a0e89484 100644 --- a/lib/private/App/AppStore/Fetcher/AppFetcher.php +++ b/lib/private/App/AppStore/Fetcher/AppFetcher.php @@ -39,12 +39,6 @@ use OCP\Support\Subscription\IRegistry; use Psr\Log\LoggerInterface; class AppFetcher extends Fetcher { - /** @var CompareVersion */ - private $compareVersion; - - /** @var IRegistry */ - protected $registry; - /** @var bool */ private $ignoreMaxVersion; @@ -52,9 +46,10 @@ class AppFetcher extends Fetcher { IClientService $clientService, ITimeFactory $timeFactory, IConfig $config, - CompareVersion $compareVersion, + private CompareVersion $compareVersion, LoggerInterface $logger, - IRegistry $registry) { + protected IRegistry $registry, + ) { parent::__construct( $appDataFactory, $clientService, @@ -64,9 +59,6 @@ class AppFetcher extends Fetcher { $registry ); - $this->compareVersion = $compareVersion; - $this->registry = $registry; - $this->fileName = 'apps.json'; $this->endpointName = 'apps.json'; $this->ignoreMaxVersion = true; diff --git a/lib/private/App/AppStore/Fetcher/CategoryFetcher.php b/lib/private/App/AppStore/Fetcher/CategoryFetcher.php index d1bbe4f7b04..9b84d2a6196 100644 --- a/lib/private/App/AppStore/Fetcher/CategoryFetcher.php +++ b/lib/private/App/AppStore/Fetcher/CategoryFetcher.php @@ -34,12 +34,14 @@ use OCP\Support\Subscription\IRegistry; use Psr\Log\LoggerInterface; class CategoryFetcher extends Fetcher { - public function __construct(Factory $appDataFactory, + public function __construct( + Factory $appDataFactory, IClientService $clientService, ITimeFactory $timeFactory, IConfig $config, LoggerInterface $logger, - IRegistry $registry) { + IRegistry $registry, + ) { parent::__construct( $appDataFactory, $clientService, diff --git a/lib/private/App/AppStore/Fetcher/Fetcher.php b/lib/private/App/AppStore/Fetcher/Fetcher.php index a693804f50f..3fc5fd07573 100644 --- a/lib/private/App/AppStore/Fetcher/Fetcher.php +++ b/lib/private/App/AppStore/Fetcher/Fetcher.php @@ -47,16 +47,6 @@ abstract class Fetcher { /** @var IAppData */ protected $appData; - /** @var IClientService */ - protected $clientService; - /** @var ITimeFactory */ - protected $timeFactory; - /** @var IConfig */ - protected $config; - /** @var LoggerInterface */ - protected $logger; - /** @var IRegistry */ - protected $registry; /** @var string */ protected $fileName; @@ -67,18 +57,15 @@ abstract class Fetcher { /** @var ?string */ protected $channel = null; - public function __construct(Factory $appDataFactory, - IClientService $clientService, - ITimeFactory $timeFactory, - IConfig $config, - LoggerInterface $logger, - IRegistry $registry) { + public function __construct( + Factory $appDataFactory, + protected IClientService $clientService, + protected ITimeFactory $timeFactory, + protected IConfig $config, + protected LoggerInterface $logger, + protected IRegistry $registry, + ) { $this->appData = $appDataFactory->get('appstore'); - $this->clientService = $clientService; - $this->timeFactory = $timeFactory; - $this->config = $config; - $this->logger = $logger; - $this->registry = $registry; } /** diff --git a/lib/private/App/AppStore/Version/Version.php b/lib/private/App/AppStore/Version/Version.php index d41ca1770f0..5bd0226528f 100644 --- a/lib/private/App/AppStore/Version/Version.php +++ b/lib/private/App/AppStore/Version/Version.php @@ -23,18 +23,14 @@ namespace OC\App\AppStore\Version; class Version { - /** @var string */ - private $minVersion; - /** @var string */ - private $maxVersion; - /** * @param string $minVersion * @param string $maxVersion */ - public function __construct($minVersion, $maxVersion) { - $this->minVersion = $minVersion; - $this->maxVersion = $maxVersion; + public function __construct( + private string $minVersion, + private string $maxVersion, + ) { } /** diff --git a/lib/private/App/DependencyAnalyzer.php b/lib/private/App/DependencyAnalyzer.php index 3bdc551ea5a..2974e31dc5d 100644 --- a/lib/private/App/DependencyAnalyzer.php +++ b/lib/private/App/DependencyAnalyzer.php @@ -33,10 +33,6 @@ namespace OC\App; use OCP\IL10N; class DependencyAnalyzer { - /** @var Platform */ - private $platform; - /** @var \OCP\IL10N */ - private $l; /** @var array */ private $appInfo; @@ -44,9 +40,10 @@ class DependencyAnalyzer { * @param Platform $platform * @param \OCP\IL10N $l */ - public function __construct(Platform $platform, IL10N $l) { - $this->platform = $platform; - $this->l = $l; + public function __construct( + private Platform $platform, + private IL10N $l, + ) { } /** diff --git a/lib/private/App/InfoParser.php b/lib/private/App/InfoParser.php index 79d051fd2a1..2461d587bbd 100644 --- a/lib/private/App/InfoParser.php +++ b/lib/private/App/InfoParser.php @@ -34,14 +34,12 @@ use function libxml_disable_entity_loader; use function simplexml_load_string; class InfoParser { - /** @var \OCP\ICache|null */ - private $cache; - /** * @param ICache|null $cache */ - public function __construct(ICache $cache = null) { - $this->cache = $cache; + public function __construct( + private ?ICache $cache = null, + ) { } /** diff --git a/lib/private/App/Platform.php b/lib/private/App/Platform.php index daff247d1bd..c5565e9e0c2 100644 --- a/lib/private/App/Platform.php +++ b/lib/private/App/Platform.php @@ -36,10 +36,9 @@ use OCP\IConfig; * @package OC\App */ class Platform { - private IConfig $config; - - public function __construct(IConfig $config) { - $this->config = $config; + public function __construct( + private IConfig $config, + ) { } public function getPhpVersion(): string { diff --git a/lib/private/AppFramework/OCS/BaseResponse.php b/lib/private/AppFramework/OCS/BaseResponse.php index 3cfe8177ae7..78bcc5586d3 100644 --- a/lib/private/AppFramework/OCS/BaseResponse.php +++ b/lib/private/AppFramework/OCS/BaseResponse.php @@ -159,6 +159,10 @@ abstract class BaseResponse extends Response { $writer->startElement($k); $this->toXML($v, $writer); $writer->endElement(); + } elseif ($v instanceof \JsonSerializable) { + $writer->startElement($k); + $this->toXML($v->jsonSerialize(), $writer); + $writer->endElement(); } else { $writer->writeElement($k, $v); } diff --git a/lib/private/AppFramework/Routing/RouteConfig.php b/lib/private/AppFramework/Routing/RouteConfig.php index 6e3e49e8d99..7d63e5477ce 100644 --- a/lib/private/AppFramework/Routing/RouteConfig.php +++ b/lib/private/AppFramework/Routing/RouteConfig.php @@ -136,7 +136,13 @@ class RouteConfig { $controllerName = $this->buildControllerName($controller); $actionName = $this->buildActionName($action); - $routeName = $routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix; + /* + * The route name has to be lowercase, for symfony to match it correctly. + * This is required because smyfony allows mixed casing for controller names in the routes. + * To avoid breaking all the existing route names, registering and matching will only use the lowercase names. + * This is also safe on the PHP side because class and method names collide regardless of the casing. + */ + $routeName = strtolower($routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix); $router = $this->router->create($routeName, $url) ->method($verb); diff --git a/lib/private/AppFramework/Routing/RouteParser.php b/lib/private/AppFramework/Routing/RouteParser.php index 1b3a6c1255a..1b05c23df9d 100644 --- a/lib/private/AppFramework/Routing/RouteParser.php +++ b/lib/private/AppFramework/Routing/RouteParser.php @@ -100,7 +100,13 @@ class RouteParser { $controllerName = $this->buildControllerName($controller); $actionName = $this->buildActionName($action); - $routeName = $routeNamePrefix . $appName . '.' . $controller . '.' . $action . $postfix; + /* + * The route name has to be lowercase, for symfony to match it correctly. + * This is required because smyfony allows mixed casing for controller names in the routes. + * To avoid breaking all the existing route names, registering and matching will only use the lowercase names. + * This is also safe on the PHP side because class and method names collide regardless of the casing. + */ + $routeName = strtolower($routeNamePrefix . $appName . '.' . $controller . '.' . $action . $postfix); $routeObject = new Route($url); $routeObject->method($verb); diff --git a/lib/private/Encryption/Keys/Storage.php b/lib/private/Encryption/Keys/Storage.php index e88c305eeec..cc7ed2f1f7b 100644 --- a/lib/private/Encryption/Keys/Storage.php +++ b/lib/private/Encryption/Keys/Storage.php @@ -98,14 +98,14 @@ class Storage implements IStorage { */ public function getFileKey($path, $keyId, $encryptionModuleId) { $realFile = $this->util->stripPartialFileExtension($path); - $keyDir = $this->getFileKeyDir($encryptionModuleId, $realFile); + $keyDir = $this->util->getFileKeyDir($encryptionModuleId, $realFile); $key = $this->getKey($keyDir . $keyId)['key']; if ($key === '' && $realFile !== $path) { // Check if the part file has keys and use them, if no normal keys // exist. This is required to fix copyBetweenStorage() when we // rename a .part file over storage borders. - $keyDir = $this->getFileKeyDir($encryptionModuleId, $path); + $keyDir = $this->util->getFileKeyDir($encryptionModuleId, $path); $key = $this->getKey($keyDir . $keyId)['key']; } @@ -135,7 +135,7 @@ class Storage implements IStorage { * @inheritdoc */ public function setFileKey($path, $keyId, $key, $encryptionModuleId) { - $keyDir = $this->getFileKeyDir($encryptionModuleId, $path); + $keyDir = $this->util->getFileKeyDir($encryptionModuleId, $path); return $this->setKey($keyDir . $keyId, [ 'key' => base64_encode($key), ]); @@ -177,7 +177,7 @@ class Storage implements IStorage { * @inheritdoc */ public function deleteFileKey($path, $keyId, $encryptionModuleId) { - $keyDir = $this->getFileKeyDir($encryptionModuleId, $path); + $keyDir = $this->util->getFileKeyDir($encryptionModuleId, $path); return !$this->view->file_exists($keyDir . $keyId) || $this->view->unlink($keyDir . $keyId); } @@ -185,7 +185,7 @@ class Storage implements IStorage { * @inheritdoc */ public function deleteAllFileKeys($path) { - $keyDir = $this->getFileKeyDir('', $path); + $keyDir = $this->util->getFileKeyDir('', $path); return !$this->view->file_exists($keyDir) || $this->view->deleteAll($keyDir); } @@ -356,26 +356,6 @@ class Storage implements IStorage { } /** - * get path to key folder for a given file - * - * @param string $encryptionModuleId - * @param string $path path to the file, relative to data/ - * @return string - */ - private function getFileKeyDir($encryptionModuleId, $path) { - [$owner, $filename] = $this->util->getUidAndFilename($path); - - // in case of system wide mount points the keys are stored directly in the data directory - if ($this->util->isSystemWideMountPoint($filename, $owner)) { - $keyPath = $this->root_dir . '/' . $this->keys_base_dir . $filename . '/'; - } else { - $keyPath = $this->root_dir . '/' . $owner . $this->keys_base_dir . $filename . '/'; - } - - return Filesystem::normalizePath($keyPath . $encryptionModuleId . '/', false); - } - - /** * move keys if a file was renamed * * @param string $source diff --git a/lib/private/Encryption/Util.php b/lib/private/Encryption/Util.php index a828483265b..bd27d71c40e 100644 --- a/lib/private/Encryption/Util.php +++ b/lib/private/Encryption/Util.php @@ -385,4 +385,25 @@ class Util { return $result; } + + /** + * get path to key folder for a given file + * + * @param string $encryptionModuleId + * @param string $path path to the file, relative to data/ + * @return string + */ + public function getFileKeyDir(string $encryptionModuleId, string $path): string { + [$owner, $filename] = $this->getUidAndFilename($path); + $root = $this->getKeyStorageRoot(); + + // in case of system-wide mount points the keys are stored directly in the data directory + if ($this->isSystemWideMountPoint($filename, $owner)) { + $keyPath = $root . '/' . '/files_encryption/keys' . $filename . '/'; + } else { + $keyPath = $root . '/' . $owner . '/files_encryption/keys' . $filename . '/'; + } + + return Filesystem::normalizePath($keyPath . $encryptionModuleId . '/', false); + } } diff --git a/lib/private/EventDispatcher/EventDispatcher.php b/lib/private/EventDispatcher/EventDispatcher.php index 14c13d516c0..39bf2a6afa9 100644 --- a/lib/private/EventDispatcher/EventDispatcher.php +++ b/lib/private/EventDispatcher/EventDispatcher.php @@ -33,29 +33,17 @@ use OCP\Broadcast\Events\IBroadcastEvent; use OCP\EventDispatcher\ABroadcastedEvent; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; -use OCP\IContainer; use OCP\IServerContainer; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcher as SymfonyDispatcher; use function get_class; class EventDispatcher implements IEventDispatcher { - /** @var SymfonyDispatcher */ - private $dispatcher; - - /** @var IContainer */ - private $container; - - /** @var LoggerInterface */ - private $logger; - - public function __construct(SymfonyDispatcher $dispatcher, - IServerContainer $container, - LoggerInterface $logger) { - $this->dispatcher = $dispatcher; - $this->container = $container; - $this->logger = $logger; - + public function __construct( + private SymfonyDispatcher $dispatcher, + private IServerContainer $container, + private LoggerInterface $logger, + ) { // inject the event dispatcher into the logger // this is done here because there is a cyclic dependency between the event dispatcher and logger if ($this->logger instanceof Log || $this->logger instanceof Log\PsrLoggerAdapter) { @@ -86,6 +74,10 @@ class EventDispatcher implements IEventDispatcher { $this->addListener($eventName, $listener, $priority); } + public function hasListeners(string $eventName): bool { + return $this->dispatcher->hasListeners($eventName); + } + /** * @deprecated */ diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php index 511e80bd7d9..93b7dc37b6b 100644 --- a/lib/private/Files/SetupManager.php +++ b/lib/private/Files/SetupManager.php @@ -80,6 +80,7 @@ class SetupManager { private bool $listeningForProviders; private array $fullSetupRequired = []; private bool $setupBuiltinWrappersDone = false; + private bool $forceFullSetup = false; public function __construct( private IEventLogger $eventLogger, @@ -97,6 +98,7 @@ class SetupManager { ) { $this->cache = $cacheFactory->createDistributed('setupmanager::'); $this->listeningForProviders = false; + $this->forceFullSetup = $this->config->getSystemValueBool('debug.force-full-fs-setup'); $this->setupListeners(); } @@ -470,6 +472,10 @@ class SetupManager { } private function fullSetupRequired(IUser $user): bool { + if ($this->forceFullSetup) { + return true; + } + // we perform a "cached" setup only after having done the full setup recently // this is also used to trigger a full setup after handling events that are likely // to change the available mounts diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index 65c2580e61c..830f0aaded7 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -62,6 +62,7 @@ use OCP\Files\ReservedWordException; use OCP\Files\Storage\ILockingStorage; use OCP\Files\Storage\IStorage; use OCP\Files\Storage\IWriteStreamStorage; +use OCP\Files\StorageNotAvailableException; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; use OCP\Server; @@ -898,6 +899,11 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { public function getDirectoryContent($directory): \Traversable { $dh = $this->opendir($directory); + + if ($dh === false) { + throw new StorageNotAvailableException('Directory listing failed'); + } + if (is_resource($dh)) { $basePath = rtrim($directory, '/'); while (($file = readdir($dh)) !== false) { diff --git a/lib/private/FilesMetadata/Model/FilesMetadata.php b/lib/private/FilesMetadata/Model/FilesMetadata.php index 629b537dabe..84cb177bc37 100644 --- a/lib/private/FilesMetadata/Model/FilesMetadata.php +++ b/lib/private/FilesMetadata/Model/FilesMetadata.php @@ -480,7 +480,7 @@ class FilesMetadata implements IFilesMetadata { // if value does not exist, or type has changed, we keep on the writing } - $valueWrapper = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_STRING_LIST); + $valueWrapper = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_INT_LIST); $this->metadata[$key] = $valueWrapper->setValueIntList($value)->setIndexed($index); $this->updated = true; diff --git a/lib/private/FullTextSearch/FullTextSearchManager.php b/lib/private/FullTextSearch/FullTextSearchManager.php index 8d850513949..acaf519209b 100644 --- a/lib/private/FullTextSearch/FullTextSearchManager.php +++ b/lib/private/FullTextSearch/FullTextSearchManager.php @@ -39,47 +39,35 @@ use OCP\FullTextSearch\Service\ISearchService; * @package OC\FullTextSearch */ class FullTextSearchManager implements IFullTextSearchManager { - /** @var IProviderService */ - private $providerService; + private ?IProviderService $providerService; - /** @var IIndexService */ - private $indexService; - - /** @var ISearchService */ - private $searchService; + private ?IIndexService $indexService; + private ?ISearchService $searchService; /** * @since 15.0.0 - * - * @param IProviderService $providerService */ - public function registerProviderService(IProviderService $providerService) { + public function registerProviderService(IProviderService $providerService): void { $this->providerService = $providerService; } /** * @since 15.0.0 - * - * @param IIndexService $indexService */ - public function registerIndexService(IIndexService $indexService) { + public function registerIndexService(IIndexService $indexService): void { $this->indexService = $indexService; } /** * @since 15.0.0 - * - * @param ISearchService $searchService */ - public function registerSearchService(ISearchService $searchService) { + public function registerSearchService(ISearchService $searchService): void { $this->searchService = $searchService; } /** * @since 16.0.0 - * - * @return bool */ public function isAvailable(): bool { if ($this->indexService === null || @@ -93,7 +81,6 @@ class FullTextSearchManager implements IFullTextSearchManager { /** - * @return IProviderService * @throws FullTextSearchAppNotAvailableException */ private function getProviderService(): IProviderService { @@ -106,7 +93,6 @@ class FullTextSearchManager implements IFullTextSearchManager { /** - * @return IIndexService * @throws FullTextSearchAppNotAvailableException */ private function getIndexService(): IIndexService { @@ -119,7 +105,6 @@ class FullTextSearchManager implements IFullTextSearchManager { /** - * @return ISearchService * @throws FullTextSearchAppNotAvailableException */ private function getSearchService(): ISearchService { @@ -134,15 +119,12 @@ class FullTextSearchManager implements IFullTextSearchManager { /** * @throws FullTextSearchAppNotAvailableException */ - public function addJavascriptAPI() { + public function addJavascriptAPI(): void { $this->getProviderService()->addJavascriptAPI(); } /** - * @param string $providerId - * - * @return bool * @throws FullTextSearchAppNotAvailableException */ public function isProviderIndexed(string $providerId): bool { @@ -151,9 +133,6 @@ class FullTextSearchManager implements IFullTextSearchManager { /** - * @param string $providerId - * @param string $documentId - * @return IIndex * @throws FullTextSearchAppNotAvailableException */ public function getIndex(string $providerId, string $documentId): IIndex { @@ -161,46 +140,45 @@ class FullTextSearchManager implements IFullTextSearchManager { } /** - * @param string $providerId - * @param string $documentId - * @param string $userId - * @param int $status - * * @see IIndex for available value for $status. * - * @return IIndex * @throws FullTextSearchAppNotAvailableException */ - public function createIndex(string $providerId, string $documentId, string $userId, int $status = 0): IIndex { + public function createIndex( + string $providerId, + string $documentId, + string $userId, + int $status = 0, + ): IIndex { return $this->getIndexService()->createIndex($providerId, $documentId, $userId, $status); } /** - * @param string $providerId - * @param string $documentId - * @param int $status - * @param bool $reset - * * @see IIndex for available value for $status. * * @throws FullTextSearchAppNotAvailableException */ - public function updateIndexStatus(string $providerId, string $documentId, int $status, bool $reset = false) { + public function updateIndexStatus( + string $providerId, + string $documentId, + int $status, + bool $reset = false, + ): void { $this->getIndexService()->updateIndexStatus($providerId, $documentId, $status, $reset); } /** - * @param string $providerId - * @param array $documentIds - * @param int $status - * @param bool $reset - * * @see IIndex for available value for $status. * * @throws FullTextSearchAppNotAvailableException */ - public function updateIndexesStatus(string $providerId, array $documentIds, int $status, bool $reset = false) { + public function updateIndexesStatus( + string $providerId, + array $documentIds, + int $status, + bool $reset = false, + ): void { $this->getIndexService()->updateIndexesStatus($providerId, $documentIds, $status, $reset); } @@ -210,15 +188,12 @@ class FullTextSearchManager implements IFullTextSearchManager { * * @throws FullTextSearchAppNotAvailableException */ - public function updateIndexes(array $indexes) { + public function updateIndexes(array $indexes): void { $this->getIndexService()->updateIndexes($indexes); } /** - * @param array $request - * @param string $userId - * * @return ISearchResult[] * @throws FullTextSearchAppNotAvailableException */ diff --git a/lib/private/FullTextSearch/Model/DocumentAccess.php b/lib/private/FullTextSearch/Model/DocumentAccess.php index cb2b95284f1..eecd038ca03 100644 --- a/lib/private/FullTextSearch/Model/DocumentAccess.php +++ b/lib/private/FullTextSearch/Model/DocumentAccess.php @@ -49,23 +49,17 @@ use OCP\FullTextSearch\Model\IDocumentAccess; * @package OC\FullTextSearch\Model */ final class DocumentAccess implements IDocumentAccess, JsonSerializable { - /** @var string */ - private $ownerId; + private string $ownerId; - /** @var string */ - private $viewerId = ''; + private string $viewerId = ''; - /** @var array */ - private $users = []; + private array $users = []; - /** @var array */ - private $groups = []; + private array $groups = []; - /** @var array */ - private $circles = []; + private array $circles = []; - /** @var array */ - private $links = []; + private array $links = []; /** @@ -74,8 +68,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * @since 16.0.0 * * IDocumentAccess constructor. - * - * @param string $ownerId */ public function __construct(string $ownerId = '') { $this->setOwnerId($ownerId); @@ -86,10 +78,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Set the Owner of the document. * * @since 16.0.0 - * - * @param string $ownerId - * - * @return IDocumentAccess */ public function setOwnerId(string $ownerId): IDocumentAccess { $this->ownerId = $ownerId; @@ -101,8 +89,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Get the Owner of the document. * * @since 16.0.0 - * - * @return string */ public function getOwnerId(): string { return $this->ownerId; @@ -113,10 +99,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Set the viewer of the document. * * @since 16.0.0 - * - * @param string $viewerId - * - * @return IDocumentAccess */ public function setViewerId(string $viewerId): IDocumentAccess { $this->viewerId = $viewerId; @@ -128,8 +110,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Get the viewer of the document. * * @since 16.0.0 - * - * @return string */ public function getViewerId(): string { return $this->viewerId; @@ -140,10 +120,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Set the list of users that have read access to the document. * * @since 16.0.0 - * - * @param array $users - * - * @return IDocumentAccess */ public function setUsers(array $users): IDocumentAccess { $this->users = $users; @@ -155,10 +131,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Add an entry to the list of users that have read access to the document. * * @since 16.0.0 - * - * @param string $user - * - * @return IDocumentAccess */ public function addUser(string $user): IDocumentAccess { $this->users[] = $user; @@ -171,10 +143,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * document. * * @since 16.0.0 - * - * @param array $users - * - * @return IDocumentAccess */ public function addUsers($users): IDocumentAccess { $this->users = array_merge($this->users, $users); @@ -186,8 +154,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Get the complete list of users that have read access to the document. * * @since 16.0.0 - * - * @return array */ public function getUsers(): array { return $this->users; @@ -198,10 +164,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Set the list of groups that have read access to the document. * * @since 16.0.0 - * - * @param array $groups - * - * @return IDocumentAccess */ public function setGroups(array $groups): IDocumentAccess { $this->groups = $groups; @@ -213,10 +175,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Add an entry to the list of groups that have read access to the document. * * @since 16.0.0 - * - * @param string $group - * - * @return IDocumentAccess */ public function addGroup(string $group): IDocumentAccess { $this->groups[] = $group; @@ -229,12 +187,8 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * document. * * @since 16.0.0 - * - * @param array $groups - * - * @return IDocumentAccess */ - public function addGroups(array $groups) { + public function addGroups(array $groups): IDocumentAccess { $this->groups = array_merge($this->groups, $groups); return $this; @@ -244,8 +198,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Get the complete list of groups that have read access to the document. * * @since 16.0.0 - * - * @return array */ public function getGroups(): array { return $this->groups; @@ -256,10 +208,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Set the list of circles that have read access to the document. * * @since 16.0.0 - * - * @param array $circles - * - * @return IDocumentAccess */ public function setCircles(array $circles): IDocumentAccess { $this->circles = $circles; @@ -271,10 +219,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Add an entry to the list of circles that have read access to the document. * * @since 16.0.0 - * - * @param string $circle - * - * @return IDocumentAccess */ public function addCircle(string $circle): IDocumentAccess { $this->circles[] = $circle; @@ -287,10 +231,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * document. * * @since 16.0.0 - * - * @param array $circles - * - * @return IDocumentAccess */ public function addCircles(array $circles): IDocumentAccess { $this->circles = array_merge($this->circles, $circles); @@ -302,8 +242,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Get the complete list of circles that have read access to the document. * * @since 16.0.0 - * - * @return array */ public function getCircles(): array { return $this->circles; @@ -314,10 +252,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Set the list of links that have read access to the document. * * @since 16.0.0 - * - * @param array $links - * - * @return IDocumentAccess */ public function setLinks(array $links): IDocumentAccess { $this->links = $links; @@ -329,8 +263,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Get the list of links that have read access to the document. * * @since 16.0.0 - * - * @return array */ public function getLinks(): array { return $this->links; @@ -339,8 +271,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { /** * @since 16.0.0 - * - * @return array */ public function jsonSerialize(): array { return [ diff --git a/lib/private/FullTextSearch/Model/IndexDocument.php b/lib/private/FullTextSearch/Model/IndexDocument.php index 74788463693..1b2e0eb5896 100644 --- a/lib/private/FullTextSearch/Model/IndexDocument.php +++ b/lib/private/FullTextSearch/Model/IndexDocument.php @@ -26,6 +26,7 @@ declare(strict_types=1); namespace OC\FullTextSearch\Model; use JsonSerializable; +use OCP\FullTextSearch\Exceptions\FullTextSearchIndexNotAvailableException; use OCP\FullTextSearch\Model\IDocumentAccess; use OCP\FullTextSearch\Model\IIndex; use OCP\FullTextSearch\Model\IIndexDocument; @@ -47,62 +48,41 @@ use OCP\FullTextSearch\Model\IIndexDocument; * @package OC\FullTextSearch\Model */ class IndexDocument implements IIndexDocument, JsonSerializable { - /** @var string */ - protected $id = ''; + protected string $id = ''; - /** @var string */ - protected $providerId = ''; + protected DocumentAccess $access; - /** @var DocumentAccess */ - protected $access; + protected ?IIndex $index = null; - /** @var IIndex */ - protected $index; + protected int $modifiedTime = 0; - /** @var int */ - protected $modifiedTime = 0; + protected string $source = ''; - /** @var string */ - protected $source = ''; + protected array $tags = []; - /** @var array */ - protected $tags = []; + protected array $metaTags = []; - /** @var array */ - protected $metaTags = []; + protected array $subTags = []; - /** @var array */ - protected $subTags = []; + protected string $title = ''; - /** @var string */ - protected $title = ''; + protected string $content = ''; - /** @var string */ - protected $content = ''; + protected string $hash = ''; - /** @var string */ - protected $hash = ''; + protected array $parts = []; - /** @var array */ - protected $parts = []; + protected string $link = ''; - /** @var string */ - protected $link = ''; + protected array $more = []; - /** @var array */ - protected $more = []; + protected array $excerpts = []; - /** @var array */ - protected $excerpts = []; + protected string $score = ''; - /** @var string */ - protected $score = ''; + protected array $info = []; - /** @var array */ - protected $info = []; - - /** @var int */ - protected $contentEncoded = 0; + protected int $contentEncoded = 0; /** @@ -112,12 +92,11 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * and the Id of the original document. * * @since 15.0.0 - * - * @param string $providerId - * @param string $documentId */ - public function __construct(string $providerId, string $documentId) { - $this->providerId = $providerId; + public function __construct( + protected string $providerId, + string $documentId, + ) { $this->id = $documentId; } @@ -126,8 +105,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Returns the Id of the original document. * * @since 15.0.0 - * - * @return string */ final public function getId(): string { return $this->id; @@ -138,8 +115,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Returns the Id of the provider. * * @since 15.0.0 - * - * @return string */ final public function getProviderId(): string { return $this->providerId; @@ -152,10 +127,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * @see IIndex * * @since 15.0.0 - * - * @param IIndex $index - * - * @return IIndexDocument */ final public function setIndex(IIndex $index): IIndexDocument { $this->index = $index; @@ -166,11 +137,14 @@ class IndexDocument implements IIndexDocument, JsonSerializable { /** * Get the Index. * + * @throws FullTextSearchIndexNotAvailableException * @since 15.0.0 - * - * @return IIndex */ final public function getIndex(): IIndex { + if ($this->index === null) { + throw new FullTextSearchIndexNotAvailableException('No IIndex generated'); + } + return $this->index; } @@ -178,22 +152,15 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * return if Index is defined. * * @since 16.0.0 - * - * @return bool */ final public function hasIndex(): bool { - return ($this->index !== null); + return $this->index !== null; } - /** * Set the modified time of the original document. * * @since 15.0.0 - * - * @param int $modifiedTime - * - * @return IIndexDocument */ final public function setModifiedTime(int $modifiedTime): IIndexDocument { $this->modifiedTime = $modifiedTime; @@ -205,8 +172,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the modified time of the original document. * * @since 15.0.0 - * - * @return int */ final public function getModifiedTime(): int { return $this->modifiedTime; @@ -216,10 +181,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Check if the original document of the IIndexDocument is older than $time. * * @since 15.0.0 - * - * @param int $time - * - * @return bool */ final public function isOlderThan(int $time): bool { return ($this->modifiedTime < $time); @@ -232,10 +193,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * @see IDocumentAccess * * @since 15.0.0 - * - * @param IDocumentAccess $access - * - * @return $this */ final public function setAccess(IDocumentAccess $access): IIndexDocument { $this->access = $access; @@ -247,8 +204,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the IDocumentAccess related to the original document. * * @since 15.0.0 - * - * @return IDocumentAccess */ final public function getAccess(): IDocumentAccess { return $this->access; @@ -259,10 +214,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Add a tag to the list. * * @since 15.0.0 - * - * @param string $tag - * - * @return IIndexDocument */ final public function addTag(string $tag): IIndexDocument { $this->tags[] = $tag; @@ -274,10 +225,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set the list of tags assigned to the original document. * * @since 15.0.0 - * - * @param array $tags - * - * @return IIndexDocument */ final public function setTags(array $tags): IIndexDocument { $this->tags = $tags; @@ -289,8 +236,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the list of tags assigned to the original document. * * @since 15.0.0 - * - * @return array */ final public function getTags(): array { return $this->tags; @@ -301,10 +246,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Add a meta tag to the list. * * @since 15.0.0 - * - * @param string $tag - * - * @return IIndexDocument */ final public function addMetaTag(string $tag): IIndexDocument { $this->metaTags[] = $tag; @@ -316,10 +257,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set the list of meta tags assigned to the original document. * * @since 15.0.0 - * - * @param array $tags - * - * @return IIndexDocument */ final public function setMetaTags(array $tags): IIndexDocument { $this->metaTags = $tags; @@ -331,8 +268,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the list of meta tags assigned to the original document. * * @since 15.0.0 - * - * @return array */ final public function getMetaTags(): array { return $this->metaTags; @@ -343,11 +278,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Add a sub tag to the list. * * @since 15.0.0 - * - * @param string $sub - * @param string $tag - * - * @return IIndexDocument */ final public function addSubTag(string $sub, string $tag): IIndexDocument { if (!array_key_exists($sub, $this->subTags)) { @@ -364,10 +294,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set the list of sub tags assigned to the original document. * * @since 15.0.0 - * - * @param array $tags - * - * @return IIndexDocument */ final public function setSubTags(array $tags): IIndexDocument { $this->subTags = $tags; @@ -381,10 +307,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * dimensional array. * * @since 15.0.0 - * - * @param bool $formatted - * - * @return array */ final public function getSubTags(bool $formatted = false): array { if ($formatted === false) { @@ -408,10 +330,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set the source of the original document. * * @since 15.0.0 - * - * @param string $source - * - * @return IIndexDocument */ final public function setSource(string $source): IIndexDocument { $this->source = $source; @@ -423,8 +341,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the source of the original document. * * @since 15.0.0 - * - * @return string */ final public function getSource(): string { return $this->source; @@ -435,10 +351,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set the title of the original document. * * @since 15.0.0 - * - * @param string $title - * - * @return IIndexDocument */ final public function setTitle(string $title): IIndexDocument { $this->title = $title; @@ -450,8 +362,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the title of the original document. * * @since 15.0.0 - * - * @return string */ final public function getTitle(): string { return $this->title; @@ -464,11 +374,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * encoded in base64. * * @since 15.0.0 - * - * @param string $content - * @param int $encoded - * - * @return IIndexDocument */ final public function setContent(string $content, int $encoded = 0): IIndexDocument { $this->content = $content; @@ -481,8 +386,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the content of the original document. * * @since 15.0.0 - * - * @return string */ final public function getContent(): string { return $this->content; @@ -492,8 +395,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Returns the type of the encoding on the content. * * @since 15.0.0 - * - * @return int */ final public function isContentEncoded(): int { return $this->contentEncoded; @@ -503,8 +404,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Return the size of the content. * * @since 15.0.0 - * - * @return int */ final public function getContentSize(): int { return strlen($this->getContent()); @@ -512,11 +411,9 @@ class IndexDocument implements IIndexDocument, JsonSerializable { /** - * Generate an hash, based on the content of the original document. + * Generate a hash, based on the content of the original document. * * @since 15.0.0 - * - * @return IIndexDocument */ final public function initHash(): IIndexDocument { if ($this->getContent() === '' || is_null($this->getContent())) { @@ -532,10 +429,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set the hash of the original document. * * @since 15.0.0 - * - * @param string $hash - * - * @return IIndexDocument */ final public function setHash(string $hash): IIndexDocument { $this->hash = $hash; @@ -547,8 +440,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the hash of the original document. * * @since 15.0.0 - * - * @return string */ final public function getHash(): string { return $this->hash; @@ -562,11 +453,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * $part string. * * @since 15.0.0 - * - * @param string $part - * @param string $content - * - * @return IIndexDocument */ final public function addPart(string $part, string $content): IIndexDocument { $this->parts[$part] = $content; @@ -578,10 +464,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set all parts and their content. * * @since 15.0.0 - * - * @param array $parts - * - * @return IIndexDocument */ final public function setParts(array $parts): IIndexDocument { $this->parts = $parts; @@ -593,8 +475,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get all parts of the IIndexDocument. * * @since 15.0.0 - * - * @return array */ final public function getParts(): array { return $this->parts; @@ -605,10 +485,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Add a link, usable by the frontend. * * @since 15.0.0 - * - * @param string $link - * - * @return IIndexDocument */ final public function setLink(string $link): IIndexDocument { $this->link = $link; @@ -620,8 +496,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the link. * * @since 15.0.0 - * - * @return string */ final public function getLink(): string { return $this->link; @@ -632,10 +506,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set more information that couldn't be set using other method. * * @since 15.0.0 - * - * @param array $more - * - * @return IIndexDocument */ final public function setMore(array $more): IIndexDocument { $this->more = $more; @@ -647,8 +517,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get more information. * * @since 15.0.0 - * - * @return array */ final public function getMore(): array { return $this->more; @@ -660,11 +528,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * on the search request. * * @since 16.0.0 - * - * @param string $source - * @param string $excerpt - * - * @return IIndexDocument */ final public function addExcerpt(string $source, string $excerpt): IIndexDocument { $this->excerpts[] = @@ -681,10 +544,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set all excerpts of the content of the original document. * * @since 16.0.0 - * - * @param array $excerpts - * - * @return IIndexDocument */ final public function setExcerpts(array $excerpts): IIndexDocument { $new = []; @@ -704,8 +563,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get all excerpts of the content of the original document. * * @since 15.0.0 - * - * @return array */ final public function getExcerpts(): array { return $this->excerpts; @@ -715,9 +572,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Clean excerpt. * * @since 16.0.0 - * - * @param string $excerpt - * @return string */ private function cleanExcerpt(string $excerpt): string { $excerpt = str_replace("\\n", ' ', $excerpt); @@ -736,10 +590,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * request. * * @since 15.0.0 - * - * @param string $score - * - * @return IIndexDocument */ final public function setScore(string $score): IIndexDocument { $this->score = $score; @@ -751,8 +601,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the score. * * @since 15.0.0 - * - * @return string */ final public function getScore(): string { return $this->score; @@ -767,11 +615,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * indexing. * * @since 15.0.0 - * - * @param string $info - * @param string $value - * - * @return IIndexDocument */ final public function setInfo(string $info, string $value): IIndexDocument { $this->info[$info] = $value; @@ -783,11 +626,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get an information about a document. (string) * * @since 15.0.0 - * - * @param string $info - * @param string $default - * - * @return string */ final public function getInfo(string $info, string $default = ''): string { if (!key_exists($info, $this->info)) { @@ -805,11 +643,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * indexing. * * @since 15.0.0 - * - * @param string $info - * @param array $value - * - * @return IIndexDocument */ final public function setInfoArray(string $info, array $value): IIndexDocument { $this->info[$info] = $value; @@ -821,11 +654,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get an information about a document. (array) * * @since 15.0.0 - * - * @param string $info - * @param array $default - * - * @return array */ final public function getInfoArray(string $info, array $default = []): array { if (!key_exists($info, $this->info)) { @@ -843,11 +671,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * indexing. * * @since 15.0.0 - * - * @param string $info - * @param int $value - * - * @return IIndexDocument */ final public function setInfoInt(string $info, int $value): IIndexDocument { $this->info[$info] = $value; @@ -859,11 +682,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get an information about a document. (int) * * @since 15.0.0 - * - * @param string $info - * @param int $default - * - * @return int */ final public function getInfoInt(string $info, int $default = 0): int { if (!key_exists($info, $this->info)) { @@ -881,11 +699,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * indexing. * * @since 15.0.0 - * - * @param string $info - * @param bool $value - * - * @return IIndexDocument */ final public function setInfoBool(string $info, bool $value): IIndexDocument { $this->info[$info] = $value; @@ -897,11 +710,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get an information about a document. (bool) * * @since 15.0.0 - * - * @param string $info - * @param bool $default - * - * @return bool */ final public function getInfoBool(string $info, bool $default = false): bool { if (!key_exists($info, $this->info)) { @@ -915,13 +723,11 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get all info. * * @since 15.0.0 - * - * @return array */ final public function getInfoAll(): array { $info = []; foreach ($this->info as $k => $v) { - if (substr($k, 0, 1) === '_') { + if (str_starts_with($k, '_')) { continue; } diff --git a/lib/private/FullTextSearch/Model/SearchOption.php b/lib/private/FullTextSearch/Model/SearchOption.php index 91f45db5fb4..c8fa2b22a24 100644 --- a/lib/private/FullTextSearch/Model/SearchOption.php +++ b/lib/private/FullTextSearch/Model/SearchOption.php @@ -36,22 +36,6 @@ use OCP\FullTextSearch\Model\ISearchOption; * @package OC\FullTextSearch\Model */ final class SearchOption implements ISearchOption, JsonSerializable { - /** @var string */ - private $name = ''; - - /** @var string */ - private $title = ''; - - /** @var string */ - private $type = ''; - - /** @var string */ - private $size = ''; - - /** @var string */ - private $placeholder = ''; - - /** * * * @@ -104,37 +88,28 @@ final class SearchOption implements ISearchOption, JsonSerializable { /** * ISearchOption constructor. * - * Some value can be setduring the creation of the object. + * Some value can be set during the creation of the object. * * @since 15.0.0 - * - * @param string $name - * @param string $title - * @param string $type - * @param string $size - * @param string $placeholder */ - public function __construct(string $name = '', string $title = '', string $type = '', string $size = '', string $placeholder = '') { - $this->name = $name; - $this->title = $title; - $this->type = $type; - $this->size = $size; - $this->placeholder = $placeholder; + public function __construct( + private string $name = '', + private string $title = '', + private string $type = '', + private string $size = '', + private string $placeholder = '', + ) { } /** * Set the name/key of the option. - * The string should only contains alphanumerical chars and underscore. - * The key can be retrieve when using ISearchRequest::getOption + * The string should only contain alphanumerical chars and underscore. + * The key can be retrieved when using ISearchRequest::getOption * * @see ISearchRequest::getOption * * @since 15.0.0 - * - * @param string $name - * - * @return ISearchOption */ public function setName(string $name): ISearchOption { $this->name = $name; @@ -146,8 +121,6 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Get the name/key of the option. * * @since 15.0.0 - * - * @return string */ public function getName(): string { return $this->name; @@ -158,10 +131,6 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Set the title/display name of the option. * * @since 15.0.0 - * - * @param string $title - * - * @return ISearchOption */ public function setTitle(string $title): ISearchOption { $this->title = $title; @@ -173,8 +142,6 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Get the title of the option. * * @since 15.0.0 - * - * @return string */ public function getTitle(): string { return $this->title; @@ -186,10 +153,6 @@ final class SearchOption implements ISearchOption, JsonSerializable { * $type can be ISearchOption::CHECKBOX or ISearchOption::INPUT * * @since 15.0.0 - * - * @param string $type - * - * @return ISearchOption */ public function setType(string $type): ISearchOption { $this->type = $type; @@ -201,8 +164,6 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Get the type of the option. * * @since 15.0.0 - * - * @return string */ public function getType(): string { return $this->type; @@ -214,10 +175,6 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Value can be ISearchOption::INPUT_SMALL or not defined. * * @since 15.0.0 - * - * @param string $size - * - * @return ISearchOption */ public function setSize(string $size): ISearchOption { $this->size = $size; @@ -229,23 +186,16 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Get the size of the INPUT. * * @since 15.0.0 - * - * @return string */ public function getSize(): string { return $this->size; } - /** * In case of Type is , set the placeholder to be displayed in the input * field. * * @since 15.0.0 - * - * @param string $placeholder - * - * @return ISearchOption */ public function setPlaceholder(string $placeholder): ISearchOption { $this->placeholder = $placeholder; @@ -257,18 +207,13 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Get the placeholder. * * @since 15.0.0 - * - * @return string */ public function getPlaceholder(): string { return $this->placeholder; } - /** * @since 15.0.0 - * - * @return array */ public function jsonSerialize(): array { return [ diff --git a/lib/private/FullTextSearch/Model/SearchRequestSimpleQuery.php b/lib/private/FullTextSearch/Model/SearchRequestSimpleQuery.php index c58d55b9b55..25887cd90ac 100644 --- a/lib/private/FullTextSearch/Model/SearchRequestSimpleQuery.php +++ b/lib/private/FullTextSearch/Model/SearchRequestSimpleQuery.php @@ -37,34 +37,24 @@ use OCP\FullTextSearch\Model\ISearchRequestSimpleQuery; * @package OC\FullTextSearch\Model */ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonSerializable { - /** @var int */ - private $type = 0; - - /** @var string */ - private $field = ''; - - /** @var array */ - private $values = []; + private array $values = []; /** * SearchRequestQuery constructor. * - * @param $type - * @param $field - * * @since 17.0.0 */ - public function __construct(string $field, int $type) { - $this->field = $field; - $this->type = $type; + public function __construct( + private string $field, + private int $type, + ) { } /** * Get the compare type of the query * - * @return int * @since 17.0.0 */ public function getType(): int { @@ -75,7 +65,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Get the field to apply query * - * @return string * @since 17.0.0 */ public function getField(): string { @@ -85,9 +74,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Set the field to apply query * - * @param string $field - * - * @return ISearchRequestSimpleQuery * @since 17.0.0 */ public function setField(string $field): ISearchRequestSimpleQuery { @@ -100,7 +86,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Get the value to compare (string) * - * @return array * @since 17.0.0 */ public function getValues(): array { @@ -111,9 +96,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Add value to compare (string) * - * @param string $value - * - * @return ISearchRequestSimpleQuery * @since 17.0.0 */ public function addValue(string $value): ISearchRequestSimpleQuery { @@ -125,9 +107,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Add value to compare (int) * - * @param int $value - * - * @return ISearchRequestSimpleQuery * @since 17.0.0 */ public function addValueInt(int $value): ISearchRequestSimpleQuery { @@ -139,9 +118,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Add value to compare (array) * - * @param array $value - * - * @return ISearchRequestSimpleQuery * @since 17.0.0 */ public function addValueArray(array $value): ISearchRequestSimpleQuery { @@ -153,9 +129,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Add value to compare (bool) * - * @param bool $value - * - * @return ISearchRequestSimpleQuery * @since 17.0.0 */ public function addValueBool(bool $value): ISearchRequestSimpleQuery { diff --git a/lib/private/FullTextSearch/Model/SearchTemplate.php b/lib/private/FullTextSearch/Model/SearchTemplate.php index 4bb56f24b58..f1ecc0709e5 100644 --- a/lib/private/FullTextSearch/Model/SearchTemplate.php +++ b/lib/private/FullTextSearch/Model/SearchTemplate.php @@ -56,21 +56,13 @@ use OCP\FullTextSearch\Model\ISearchTemplate; * @package OC\FullTextSearch\Model */ final class SearchTemplate implements ISearchTemplate, JsonSerializable { - /** @var string */ - private $icon = ''; - - /** @var string */ - private $css = ''; - - /** @var string */ - private $template = ''; + private string $template = ''; /** @var SearchOption[] */ - private $panelOptions = []; + private array $panelOptions = []; /** @var SearchOption[] */ - private $navigationOptions = []; - + private array $navigationOptions = []; /** * ISearchTemplate constructor. @@ -79,13 +71,11 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * creation of the object. * * @since 15.0.0 - * - * @param string $icon - * @param string $css */ - public function __construct(string $icon = '', string $css = '') { - $this->icon = $icon; - $this->css = $css; + public function __construct( + private string $icon = '', + private string $css = '', + ) { } @@ -94,10 +84,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * FullTextSearch navigation page, in front of the related Content Provider. * * @since 15.0.0 - * - * @param string $class - * - * @return ISearchTemplate */ public function setIcon(string $class): ISearchTemplate { $this->icon = $class; @@ -107,10 +93,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { /** * Get the class of the icon. - * - * @since 15.0.0 - * - * @return string */ public function getIcon(): string { return $this->icon; @@ -121,10 +103,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * Set the path of a CSS file that will be loaded when needed. * * @since 15.0.0 - * - * @param string $css - * - * @return ISearchTemplate */ public function setCss(string $css): ISearchTemplate { $this->css = $css; @@ -136,8 +114,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * Get the path of the CSS file. * * @since 15.0.0 - * - * @return string */ public function getCss(): string { return $this->css; @@ -151,10 +127,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * a way not generated by FullTextSearch * * @since 15.0.0 - * - * @param string $template - * - * @return ISearchTemplate */ public function setTemplate(string $template): ISearchTemplate { $this->template = $template; @@ -166,8 +138,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * Get the path of the template file. * * @since 15.0.0 - * - * @return string */ public function getTemplate(): string { return $this->template; @@ -181,10 +151,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * @see ISearchOption * * @since 15.0.0 - * - * @param ISearchOption $option - * - * @return ISearchTemplate */ public function addPanelOption(ISearchOption $option): ISearchTemplate { $this->panelOptions[] = $option; @@ -210,10 +176,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * @see ISearchOption * * @since 15.0.0 - * - * @param ISearchOption $option - * - * @return ISearchTemplate */ public function addNavigationOption(ISearchOption $option): ISearchTemplate { $this->navigationOptions[] = $option; @@ -225,8 +187,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * Get all options to be displayed in the FullTextSearch navigation page. * * @since 15.0.0 - * - * @return array */ public function getNavigationOptions(): array { return $this->navigationOptions; @@ -235,8 +195,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { /** * @since 15.0.0 - * - * @return array */ public function jsonSerialize(): array { return [ diff --git a/lib/private/Http/Client/Client.php b/lib/private/Http/Client/Client.php index 3bf43e6c07e..26a0a63378b 100644 --- a/lib/private/Http/Client/Client.php +++ b/lib/private/Http/Client/Client.php @@ -192,7 +192,7 @@ class Client implements IClient { throw new LocalServerException('Could not detect any host'); } if (!$this->remoteHostValidator->isValid($host)) { - throw new LocalServerException('Host violates local access rules'); + throw new LocalServerException('Host "'.$host.'" violates local access rules'); } } diff --git a/lib/private/Http/Client/DnsPinMiddleware.php b/lib/private/Http/Client/DnsPinMiddleware.php index aecccc6ce97..518281e4af0 100644 --- a/lib/private/Http/Client/DnsPinMiddleware.php +++ b/lib/private/Http/Client/DnsPinMiddleware.php @@ -147,7 +147,7 @@ class DnsPinMiddleware { foreach ($targetIps as $ip) { if ($this->ipAddressClassifier->isLocalAddress($ip)) { // TODO: continue with all non-local IPs? - throw new LocalServerException('Host violates local access rules'); + throw new LocalServerException('Host "'.$ip.'" ('.$hostName.':'.$port.') violates local access rules'); } $curlResolves["$hostName:$port"][] = $ip; } diff --git a/lib/private/Log.php b/lib/private/Log.php index 9975696ff06..2ad214ddec5 100644 --- a/lib/private/Log.php +++ b/lib/private/Log.php @@ -62,24 +62,22 @@ use function strtr; * MonoLog is an example implementing this interface. */ class Log implements ILogger, IDataLogger { - private IWriter $logger; private ?SystemConfig $config; private ?bool $logConditionSatisfied = null; private ?Normalizer $normalizer; - private ?IRegistry $crashReporters; private ?IEventDispatcher $eventDispatcher; /** * @param IWriter $logger The logger that should be used - * @param SystemConfig $config the system config object + * @param SystemConfig|null $config the system config object * @param Normalizer|null $normalizer - * @param IRegistry|null $registry + * @param IRegistry|null $crashReporters */ public function __construct( - IWriter $logger, + private IWriter $logger, SystemConfig $config = null, Normalizer $normalizer = null, - IRegistry $registry = null + private ?IRegistry $crashReporters = null ) { // FIXME: Add this for backwards compatibility, should be fixed at some point probably if ($config === null) { @@ -87,13 +85,11 @@ class Log implements ILogger, IDataLogger { } $this->config = $config; - $this->logger = $logger; if ($normalizer === null) { $this->normalizer = new Normalizer(); } else { $this->normalizer = $normalizer; } - $this->crashReporters = $registry; $this->eventDispatcher = null; } @@ -211,15 +207,18 @@ class Log implements ILogger, IDataLogger { */ public function log(int $level, string $message, array $context = []) { $minLevel = $this->getLogLevel($context); + if ($level < $minLevel + && (($this->crashReporters?->hasReporters() ?? false) === false) + && (($this->eventDispatcher?->hasListeners(BeforeMessageLoggedEvent::class) ?? false) === false)) { + return; // no crash reporter, no listeners, we can stop for lower log level + } array_walk($context, [$this->normalizer, 'format']); $app = $context['app'] ?? 'no app in context'; $entry = $this->interpolateMessage($context, $message); - if ($this->eventDispatcher) { - $this->eventDispatcher->dispatchTyped(new BeforeMessageLoggedEvent($app, $level, $entry)); - } + $this->eventDispatcher?->dispatchTyped(new BeforeMessageLoggedEvent($app, $level, $entry)); $hasBacktrace = isset($entry['exception']); $logBacktrace = $this->config->getValue('log.backtrace', false); @@ -241,9 +240,7 @@ class Log implements ILogger, IDataLogger { $this->crashReporters->delegateMessage($entry['message'], $messageContext); } } else { - if ($this->crashReporters !== null) { - $this->crashReporters->delegateBreadcrumb($entry['message'], 'log', $context); - } + $this->crashReporters?->delegateBreadcrumb($entry['message'], 'log', $context); } } catch (Throwable $e) { // make sure we dont hard crash if logging fails @@ -329,8 +326,10 @@ class Log implements ILogger, IDataLogger { $level = $context['level'] ?? ILogger::ERROR; $minLevel = $this->getLogLevel($context); - if ($level < $minLevel && ($this->crashReporters === null || !$this->crashReporters->hasReporters())) { - return; + if ($level < $minLevel + && (($this->crashReporters?->hasReporters() ?? false) === false) + && (($this->eventDispatcher?->hasListeners(BeforeMessageLoggedEvent::class) ?? false) === false)) { + return; // no crash reporter, no listeners, we can stop for lower log level } // if an error is raised before the autoloader is properly setup, we can't serialize exceptions @@ -346,12 +345,9 @@ class Log implements ILogger, IDataLogger { $data = array_merge($serializer->serializeException($exception), $data); $data = $this->interpolateMessage($data, isset($context['message']) && $context['message'] !== '' ? $context['message'] : ('Exception thrown: ' . get_class($exception)), 'CustomMessage'); - array_walk($context, [$this->normalizer, 'format']); - if ($this->eventDispatcher) { - $this->eventDispatcher->dispatchTyped(new BeforeMessageLoggedEvent($app, $level, $data)); - } + $this->eventDispatcher?->dispatchTyped(new BeforeMessageLoggedEvent($app, $level, $data)); try { if ($level >= $minLevel) { diff --git a/lib/private/Mail/Mailer.php b/lib/private/Mail/Mailer.php index 9b7b3cf117b..61dc6f9214b 100644 --- a/lib/private/Mail/Mailer.php +++ b/lib/private/Mail/Mailer.php @@ -331,7 +331,7 @@ class Mailer implements IMailer { } $binaryParam = match ($this->config->getSystemValueString('mail_sendmailmode', 'smtp')) { - 'pipe' => ' -t', + 'pipe' => ' -t -i', default => ' -bs', }; diff --git a/lib/private/Memcache/Redis.php b/lib/private/Memcache/Redis.php index 6634f325005..5e9554eb94e 100644 --- a/lib/private/Memcache/Redis.php +++ b/lib/private/Memcache/Redis.php @@ -52,6 +52,9 @@ class Redis extends Cache implements IMemcacheTTL { ], ]; + private const DEFAULT_TTL = 24 * 60 * 60; // 1 day + private const MAX_TTL = 30 * 24 * 60 * 60; // 1 month + /** * @var \Redis|\RedisCluster $cache */ @@ -83,11 +86,12 @@ class Redis extends Cache implements IMemcacheTTL { public function set($key, $value, $ttl = 0) { $value = self::encodeValue($value); - if ($ttl > 0) { - return $this->getCache()->setex($this->getPrefix() . $key, $ttl, $value); - } else { - return $this->getCache()->set($this->getPrefix() . $key, $value); + if ($ttl === 0) { + // having infinite TTL can lead to leaked keys as the prefix changes with version upgrades + $ttl = self::DEFAULT_TTL; } + $ttl = min($ttl, self::MAX_TTL); + return $this->getCache()->setex($this->getPrefix() . $key, $ttl, $value); } public function hasKey($key) { @@ -121,11 +125,14 @@ class Redis extends Cache implements IMemcacheTTL { */ public function add($key, $value, $ttl = 0) { $value = self::encodeValue($value); + if ($ttl === 0) { + // having infinite TTL can lead to leaked keys as the prefix changes with version upgrades + $ttl = self::DEFAULT_TTL; + } + $ttl = min($ttl, self::MAX_TTL); $args = ['nx']; - if ($ttl !== 0 && is_int($ttl)) { - $args['ex'] = $ttl; - } + $args['ex'] = $ttl; return $this->getCache()->set($this->getPrefix() . $key, $value, $args); } @@ -182,6 +189,11 @@ class Redis extends Cache implements IMemcacheTTL { } public function setTTL($key, $ttl) { + if ($ttl === 0) { + // having infinite TTL can lead to leaked keys as the prefix changes with version upgrades + $ttl = self::DEFAULT_TTL; + } + $ttl = min($ttl, self::MAX_TTL); $this->getCache()->expire($this->getPrefix() . $key, $ttl); } diff --git a/lib/private/Notification/Manager.php b/lib/private/Notification/Manager.php index c712d2754e2..348ddb03df9 100644 --- a/lib/private/Notification/Manager.php +++ b/lib/private/Notification/Manager.php @@ -366,6 +366,7 @@ class Manager implements IManager { } if (!$notification->isValidParsed()) { + $this->logger->info('Notification was not parsed by any notifier [app: ' . $notification->getApp() . ', subject: ' . $notification->getSubject() . ']'); throw new \InvalidArgumentException('The given notification has not been handled'); } diff --git a/lib/private/OCS/CoreCapabilities.php b/lib/private/OCS/CoreCapabilities.php index 9cead57c6a3..0e9be3460ca 100644 --- a/lib/private/OCS/CoreCapabilities.php +++ b/lib/private/OCS/CoreCapabilities.php @@ -32,20 +32,18 @@ use OCP\IURLGenerator; * @package OC\OCS */ class CoreCapabilities implements ICapability { - /** @var IConfig */ - private $config; - /** * @param IConfig $config */ - public function __construct(IConfig $config) { - $this->config = $config; + public function __construct( + private IConfig $config, + ) { } /** * Return this classes capabilities */ - public function getCapabilities() { + public function getCapabilities(): array { return [ 'core' => [ 'pollinterval' => $this->config->getSystemValue('pollinterval', 60), diff --git a/lib/private/OCS/DiscoveryService.php b/lib/private/OCS/DiscoveryService.php index f53d39465e8..9bc83d42b1a 100644 --- a/lib/private/OCS/DiscoveryService.php +++ b/lib/private/OCS/DiscoveryService.php @@ -37,10 +37,10 @@ use OCP\OCS\IDiscoveryService; class DiscoveryService implements IDiscoveryService { /** @var ICache */ - private $cache; + private ICache $cache; /** @var IClient */ - private $client; + private IClient $client; /** * @param ICacheFactory $cacheFactory diff --git a/lib/private/OCS/Exception.php b/lib/private/OCS/Exception.php index ca3c3f70430..a4e80d12dcc 100644 --- a/lib/private/OCS/Exception.php +++ b/lib/private/OCS/Exception.php @@ -23,15 +23,13 @@ namespace OC\OCS; class Exception extends \Exception { - /** @var Result */ - private $result; - - public function __construct(Result $result) { + public function __construct( + private Result $result, + ) { parent::__construct(); - $this->result = $result; } - public function getResult() { + public function getResult(): Result { return $this->result; } } diff --git a/lib/private/OCS/Provider.php b/lib/private/OCS/Provider.php index 83219c018f8..b9bcb389c57 100644 --- a/lib/private/OCS/Provider.php +++ b/lib/private/OCS/Provider.php @@ -24,26 +24,27 @@ */ namespace OC\OCS; -class Provider extends \OCP\AppFramework\Controller { - /** @var \OCP\App\IAppManager */ - private $appManager; +use OCP\App\IAppManager; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\JSONResponse; +use OCP\IRequest; +class Provider extends Controller { /** * @param string $appName - * @param \OCP\IRequest $request - * @param \OCP\App\IAppManager $appManager + * @param IRequest $request + * @param IAppManager $appManager */ public function __construct($appName, \OCP\IRequest $request, - \OCP\App\IAppManager $appManager) { + private \OCP\App\IAppManager $appManager) { parent::__construct($appName, $request); - $this->appManager = $appManager; } /** - * @return \OCP\AppFramework\Http\JSONResponse + * @return JSONResponse */ - public function buildProviderList() { + public function buildProviderList(): JSONResponse { $services = [ 'PRIVATE_DATA' => [ 'version' => 1, @@ -108,7 +109,7 @@ class Provider extends \OCP\AppFramework\Controller { ]; } - return new \OCP\AppFramework\Http\JSONResponse([ + return new JSONResponse([ 'version' => 2, 'services' => $services, ]); diff --git a/lib/private/OCS/Result.php b/lib/private/OCS/Result.php index d77e360e6d2..567fe8378a9 100644 --- a/lib/private/OCS/Result.php +++ b/lib/private/OCS/Result.php @@ -31,14 +31,13 @@ namespace OC\OCS; class Result { - /** @var array */ - protected $data; + protected array $data; /** @var null|string */ - protected $message; + protected ?string $message; /** @var int */ - protected $statusCode; + protected int $statusCode; /** @var integer */ protected $items; @@ -47,16 +46,17 @@ class Result { protected $perPage; /** @var array */ - private $headers = []; + private array $headers = []; /** * create the OCS_Result object - * @param mixed $data the data to return + * + * @param mixed|null $data the data to return * @param int $code - * @param null|string $message + * @param string|null $message * @param array $headers */ - public function __construct($data = null, $code = 100, $message = null, $headers = []) { + public function __construct(mixed $data = null, int $code = 100, string $message = null, array $headers = []) { if ($data === null) { $this->data = []; } elseif (!is_array($data)) { @@ -71,17 +71,19 @@ class Result { /** * optionally set the total number of items available + * * @param int $items */ - public function setTotalItems($items) { + public function setTotalItems(int $items): void { $this->items = $items; } /** - * optionally set the the number of items per page + * optionally set the number of items per page + * * @param int $items */ - public function setItemsPerPage($items) { + public function setItemsPerPage(int $items): void { $this->perPage = $items; } @@ -89,7 +91,7 @@ class Result { * get the status code * @return int */ - public function getStatusCode() { + public function getStatusCode(): int { return $this->statusCode; } @@ -97,7 +99,7 @@ class Result { * get the meta data for the result * @return array */ - public function getMeta() { + public function getMeta(): array { $meta = []; $meta['status'] = $this->succeeded() ? 'ok' : 'failure'; $meta['statuscode'] = $this->statusCode; @@ -115,7 +117,7 @@ class Result { * get the result data * @return array */ - public function getData() { + public function getData(): array { return $this->data; } @@ -123,17 +125,18 @@ class Result { * return bool Whether the method succeeded * @return bool */ - public function succeeded() { + public function succeeded(): bool { return ($this->statusCode == 100); } /** * Adds a new header to the response + * * @param string $name The name of the HTTP header * @param string $value The value, null will delete it * @return $this */ - public function addHeader($name, $value) { + public function addHeader(string $name, ?string $value): static { $name = trim($name); // always remove leading and trailing whitespace // to be able to reliably check for security // headers @@ -151,7 +154,7 @@ class Result { * Returns the set headers * @return array the headers */ - public function getHeaders() { + public function getHeaders(): array { return $this->headers; } } diff --git a/lib/private/Profiler/Profiler.php b/lib/private/Profiler/Profiler.php index d6749c55e3c..9cbf703c79b 100644 --- a/lib/private/Profiler/Profiler.php +++ b/lib/private/Profiler/Profiler.php @@ -44,7 +44,7 @@ class Profiler implements IProfiler { public function __construct(SystemConfig $config) { $this->enabled = $config->getValue('profiler', false); if ($this->enabled) { - $this->storage = new FileProfilerStorage($config->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/profiler'); + $this->storage = new FileProfilerStorage($config->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/__profiler'); } } diff --git a/lib/private/Route/Router.php b/lib/private/Route/Router.php index 65bbf602be0..e7e2a9f0e49 100644 --- a/lib/private/Route/Router.php +++ b/lib/private/Route/Router.php @@ -14,6 +14,7 @@ * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> + * @author Kate Döen <kate.doeen@nextcloud.com> * * @license AGPL-3.0 * @@ -32,8 +33,10 @@ */ namespace OC\Route; +use DirectoryIterator; use OC\AppFramework\Routing\RouteParser; use OCP\AppFramework\App; +use OCP\AppFramework\Http\Attribute\Route as RouteAttribute; use OCP\Diagnostics\IEventLogger; use OCP\IConfig; use OCP\IRequest; @@ -41,6 +44,9 @@ use OCP\Route\IRouter; use OCP\Util; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; +use ReflectionAttribute; +use ReflectionClass; +use ReflectionException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Exception\RouteNotFoundException; use Symfony\Component\Routing\Generator\UrlGenerator; @@ -150,6 +156,22 @@ class Router implements IRouter { } } $this->eventLogger->start('route:load:' . $requestedApp, 'Loading Routes for ' . $requestedApp); + + if ($requestedApp !== null) { + $routes = $this->getAttributeRoutes($requestedApp); + if (count($routes) > 0) { + $this->useCollection($requestedApp); + $this->setupRoutes($routes, $requestedApp); + $collection = $this->getCollection($requestedApp); + $this->root->addCollection($collection); + + // Also add the OCS collection + $collection = $this->getCollection($requestedApp . '.ocs'); + $collection->addPrefix('/ocsapp'); + $this->root->addCollection($collection); + } + } + foreach ($routingFiles as $app => $file) { if (!isset($this->loadedApps[$app])) { if (!\OC_App::isAppLoaded($app)) { @@ -173,6 +195,7 @@ class Router implements IRouter { if (!isset($this->loadedApps['core'])) { $this->loadedApps['core'] = true; $this->useCollection('root'); + $this->setupRoutes($this->getAttributeRoutes('core'), 'core'); require_once __DIR__ . '/../../../core/routes.php'; // Also add the OCS collection @@ -360,6 +383,13 @@ class Router implements IRouter { if ($absolute === false) { $referenceType = UrlGenerator::ABSOLUTE_PATH; } + /* + * The route name has to be lowercase, for symfony to match it correctly. + * This is required because smyfony allows mixed casing for controller names in the routes. + * To avoid breaking all the existing route names, registering and matching will only use the lowercase names. + * This is also safe on the PHP side because class and method names collide regardless of the casing. + */ + $name = strtolower($name); $name = $this->fixLegacyRootName($name); if (str_contains($name, '.')) { [$appName, $other] = explode('.', $name, 3); @@ -385,34 +415,79 @@ class Router implements IRouter { } protected function fixLegacyRootName(string $routeName): string { - if ($routeName === 'files.viewcontroller.showFile') { - return 'files.View.showFile'; + if ($routeName === 'files.viewcontroller.showfile') { + return 'files.view.showfile'; } - if ($routeName === 'files_sharing.sharecontroller.showShare') { - return 'files_sharing.Share.showShare'; + if ($routeName === 'files_sharing.sharecontroller.showshare') { + return 'files_sharing.share.showshare'; } - if ($routeName === 'files_sharing.sharecontroller.showAuthenticate') { - return 'files_sharing.Share.showAuthenticate'; + if ($routeName === 'files_sharing.sharecontroller.showauthenticate') { + return 'files_sharing.share.showauthenticate'; } if ($routeName === 'files_sharing.sharecontroller.authenticate') { - return 'files_sharing.Share.authenticate'; + return 'files_sharing.share.authenticate'; } - if ($routeName === 'files_sharing.sharecontroller.downloadShare') { - return 'files_sharing.Share.downloadShare'; + if ($routeName === 'files_sharing.sharecontroller.downloadshare') { + return 'files_sharing.share.downloadshare'; } - if ($routeName === 'files_sharing.publicpreview.directLink') { - return 'files_sharing.PublicPreview.directLink'; + if ($routeName === 'files_sharing.publicpreview.directlink') { + return 'files_sharing.publicpreview.directlink'; } - if ($routeName === 'cloud_federation_api.requesthandlercontroller.addShare') { - return 'cloud_federation_api.RequestHandler.addShare'; + if ($routeName === 'cloud_federation_api.requesthandlercontroller.addshare') { + return 'cloud_federation_api.requesthandler.addshare'; } - if ($routeName === 'cloud_federation_api.requesthandlercontroller.receiveNotification') { - return 'cloud_federation_api.RequestHandler.receiveNotification'; + if ($routeName === 'cloud_federation_api.requesthandlercontroller.receivenotification') { + return 'cloud_federation_api.requesthandler.receivenotification'; } return $routeName; } /** + * @throws ReflectionException + */ + private function getAttributeRoutes(string $app): array { + $routes = []; + + if ($app === 'core') { + $appControllerPath = __DIR__ . '/../../../core/Controller'; + $appNameSpace = 'OC\\Core'; + } else { + $appControllerPath = \OC_App::getAppPath($app) . '/lib/Controller'; + $appNameSpace = App::buildAppNamespace($app); + } + + if (!file_exists($appControllerPath)) { + return []; + } + + $dir = new DirectoryIterator($appControllerPath); + foreach ($dir as $file) { + if (!str_ends_with($file->getPathname(), 'Controller.php')) { + continue; + } + + $class = new ReflectionClass($appNameSpace . '\\Controller\\' . basename($file->getPathname(), '.php')); + + foreach ($class->getMethods() as $method) { + foreach ($method->getAttributes(RouteAttribute::class, ReflectionAttribute::IS_INSTANCEOF) as $attribute) { + $route = $attribute->newInstance(); + + $serializedRoute = $route->toArray(); + // Remove 'Controller' suffix + $serializedRoute['name'] = substr($class->getShortName(), 0, -10) . '#' . $method->getName(); + + $key = $route->getType(); + + $routes[$key] ??= []; + $routes[$key][] = $serializedRoute; + } + } + } + + return $routes; + } + + /** * To isolate the variable scope used inside the $file it is required in it's own method * * @param string $file the route file location to include diff --git a/lib/private/Server.php b/lib/private/Server.php index 8de4fa7d5ca..2783e6f49c0 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -716,7 +716,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService('RedisFactory', function (Server $c) { $systemConfig = $c->get(SystemConfig::class); - return new RedisFactory($systemConfig, $c->getEventLogger()); + return new RedisFactory($systemConfig, $c->get(IEventLogger::class)); }); $this->registerService(\OCP\Activity\IManager::class, function (Server $c) { @@ -1174,7 +1174,7 @@ class Server extends ServerContainer implements IServerContainer { $classExists = false; } - if ($classExists && $c->get(\OCP\IConfig::class)->getSystemValueBool('installed', false) && $c->get(IAppManager::class)->isInstalled('theming') && $c->getTrustedDomainHelper()->isTrustedDomain($c->getRequest()->getInsecureServerHost())) { + if ($classExists && $c->get(\OCP\IConfig::class)->getSystemValueBool('installed', false) && $c->get(IAppManager::class)->isInstalled('theming') && $c->get(TrustedDomainHelper::class)->isTrustedDomain($c->getRequest()->getInsecureServerHost())) { $imageManager = new ImageManager( $c->get(\OCP\IConfig::class), $c->getAppDataDir('theming'), diff --git a/lib/private/Updater.php b/lib/private/Updater.php index 62d5fd1c058..7d26fbebc4b 100644 --- a/lib/private/Updater.php +++ b/lib/private/Updater.php @@ -41,6 +41,7 @@ declare(strict_types=1); namespace OC; use OC\App\AppManager; +use OC\App\AppStore\Fetcher\AppFetcher; use OC\DB\Connection; use OC\DB\MigrationService; use OC\DB\MigratorExecuteSqlEvent; @@ -273,7 +274,7 @@ class Updater extends BasicEmitter { $this->doAppUpgrade(); // Update the appfetchers version so it downloads the correct list from the appstore - \OC::$server->getAppFetcher()->setVersion($currentVersion); + \OC::$server->get(AppFetcher::class)->setVersion($currentVersion); /** @var AppManager $appManager */ $appManager = \OC::$server->getAppManager(); diff --git a/lib/private/legacy/OC_Template.php b/lib/private/legacy/OC_Template.php index 974f26fa381..545d5a73a63 100644 --- a/lib/private/legacy/OC_Template.php +++ b/lib/private/legacy/OC_Template.php @@ -21,6 +21,7 @@ * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <vincent@nextcloud.com> + * @author Richard Steinmetz <richard@steinmetz.cloud> * * @license AGPL-3.0 * @@ -38,7 +39,9 @@ * */ use OC\TemplateLayout; +use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; use OCP\AppFramework\Http\TemplateResponse; +use OCP\EventDispatcher\IEventDispatcher; require_once __DIR__.'/template/functions.php'; @@ -249,20 +252,44 @@ class OC_Template extends \OC\Template\Base { // If the hint is the same as the message there is no need to display it twice. $hint = ''; } + $errors = [['error' => $error_msg, 'hint' => $hint]]; http_response_code($statusCode); try { - $content = new \OC_Template('', 'error', 'error', false); - $errors = [['error' => $error_msg, 'hint' => $hint]]; - $content->assign('errors', $errors); - $content->printPage(); - } catch (\Exception $e) { + // Try rendering themed html error page + $response = new TemplateResponse( + '', + 'error', + ['errors' => $errors], + TemplateResponse::RENDER_AS_ERROR, + $statusCode, + ); + $event = new BeforeTemplateRenderedEvent(false, $response); + \OC::$server->get(IEventDispatcher::class)->dispatchTyped($event); + print($response->render()); + } catch (\Throwable $e1) { $logger = \OC::$server->getLogger(); - $logger->error("$error_msg $hint", ['app' => 'core']); - $logger->logException($e, ['app' => 'core']); + $logger->logException($e1, [ + 'app' => 'core', + 'message' => 'Rendering themed error page failed. Falling back to unthemed error page.' + ]); - header('Content-Type: text/plain; charset=utf-8'); - print("$error_msg $hint"); + try { + // Try rendering unthemed html error page + $content = new \OC_Template('', 'error', 'error', false); + $content->assign('errors', $errors); + $content->printPage(); + } catch (\Exception $e2) { + // If nothing else works, fall back to plain text error page + $logger->error("$error_msg $hint", ['app' => 'core']); + $logger->logException($e2, [ + 'app' => 'core', + 'message' => 'Rendering unthemed error page failed. Falling back to plain text error page.' + ]); + + header('Content-Type: text/plain; charset=utf-8'); + print("$error_msg $hint"); + } } die(); } @@ -275,8 +302,10 @@ class OC_Template extends \OC\Template\Base { * @suppress PhanAccessMethodInternal */ public static function printExceptionErrorPage($exception, $statusCode = 503) { + $debug = false; http_response_code($statusCode); try { + $debug = \OC::$server->getSystemConfig()->getValue('debug', false); $request = \OC::$server->getRequest(); $content = new \OC_Template('', 'exception', 'error', false); $content->assign('errorClass', get_class($exception)); @@ -285,7 +314,7 @@ class OC_Template extends \OC\Template\Base { $content->assign('file', $exception->getFile()); $content->assign('line', $exception->getLine()); $content->assign('exception', $exception); - $content->assign('debugMode', \OC::$server->getSystemConfig()->getValue('debug', false)); + $content->assign('debugMode', $debug); $content->assign('remoteAddr', $request->getRemoteAddress()); $content->assign('requestID', $request->getId()); $content->printPage(); @@ -296,22 +325,28 @@ class OC_Template extends \OC\Template\Base { $logger->logException($e, ['app' => 'core']); } catch (Throwable $e) { // no way to log it properly - but to avoid a white page of death we send some output - header('Content-Type: text/plain; charset=utf-8'); - print("Internal Server Error\n\n"); - print("The server encountered an internal error and was unable to complete your request.\n"); - print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n"); - print("More details can be found in the server log.\n"); + self::printPlainErrorPage($e, $debug); // and then throw it again to log it at least to the web server error log throw $e; } - header('Content-Type: text/plain; charset=utf-8'); - print("Internal Server Error\n\n"); - print("The server encountered an internal error and was unable to complete your request.\n"); - print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n"); - print("More details can be found in the server log.\n"); + self::printPlainErrorPage($e, $debug); } die(); } + + private static function printPlainErrorPage(\Throwable $exception, bool $debug = false) { + header('Content-Type: text/plain; charset=utf-8'); + print("Internal Server Error\n\n"); + print("The server encountered an internal error and was unable to complete your request.\n"); + print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n"); + print("More details can be found in the server log.\n"); + + if ($debug) { + print("\n"); + print($exception->getMessage() . ' ' . $exception->getFile() . ' at ' . $exception->getLine() . "\n"); + print($exception->getTraceAsString()); + } + } } diff --git a/lib/public/AppFramework/Http/Attribute/ApiRoute.php b/lib/public/AppFramework/Http/Attribute/ApiRoute.php new file mode 100644 index 00000000000..0b566082521 --- /dev/null +++ b/lib/public/AppFramework/Http/Attribute/ApiRoute.php @@ -0,0 +1,63 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2024 Kate Döen <kate.doeen@nextcloud.com> + * + * @author Kate Döen <kate.doeen@nextcloud.com> + * + * @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 OCP\AppFramework\Http\Attribute; + +use Attribute; + +/** + * This attribute can be used to define API routes on controller methods. + * + * It works in addition to the traditional routes.php method and has the same parameters + * (except for the `name` parameter which is not needed). + * + * @since 29.0.0 + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +class ApiRoute extends Route { + /** + * @inheritDoc + * + * @since 29.0.0 + */ + public function __construct( + protected string $verb, + protected string $url, + protected ?array $requirements = null, + protected ?array $defaults = null, + protected ?string $root = null, + protected ?string $postfix = null, + ) { + parent::__construct( + Route::TYPE_API, + $verb, + $url, + $requirements, + $defaults, + $root, + $postfix, + ); + } +} diff --git a/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php b/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php new file mode 100644 index 00000000000..ac08513a2a9 --- /dev/null +++ b/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php @@ -0,0 +1,63 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2024 Kate Döen <kate.doeen@nextcloud.com> + * + * @author Kate Döen <kate.doeen@nextcloud.com> + * + * @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 OCP\AppFramework\Http\Attribute; + +use Attribute; + +/** + * This attribute can be used to define Frontpage routes on controller methods. + * + * It works in addition to the traditional routes.php method and has the same parameters + * (except for the `name` parameter which is not needed). + * + * @since 29.0.0 + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +class FrontpageRoute extends Route { + /** + * @inheritDoc + * + * @since 29.0.0 + */ + public function __construct( + protected string $verb, + protected string $url, + protected ?array $requirements = null, + protected ?array $defaults = null, + protected ?string $root = null, + protected ?string $postfix = null, + ) { + parent::__construct( + Route::TYPE_FRONTPAGE, + $verb, + $url, + $requirements, + $defaults, + $root, + $postfix, + ); + } +} diff --git a/lib/public/AppFramework/Http/Attribute/Route.php b/lib/public/AppFramework/Http/Attribute/Route.php new file mode 100644 index 00000000000..58579c1f956 --- /dev/null +++ b/lib/public/AppFramework/Http/Attribute/Route.php @@ -0,0 +1,161 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2024 Kate Döen <kate.doeen@nextcloud.com> + * + * @author Kate Döen <kate.doeen@nextcloud.com> + * + * @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 OCP\AppFramework\Http\Attribute; + +use Attribute; + +/** + * This attribute can be used to define routes on controller methods. + * + * It works in addition to the traditional routes.php method and has the same parameters + * (except for the `name` parameter which is not needed). + * + * @since 29.0.0 + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +class Route { + + /** + * Corresponds to the `ocs` key in routes.php + * + * @see ApiRoute + * @since 29.0.0 + */ + public const TYPE_API = 'ocs'; + + /** + * Corresponds to the `routes` key in routes.php + * + * @see FrontpageRoute + * @since 29.0.0 + */ + public const TYPE_FRONTPAGE = 'routes'; + + /** + * @param string $type Either Route::TYPE_API or Route::TYPE_FRONTPAGE. + * @psalm-param Route::TYPE_* $type + * @param string $verb HTTP method of the route. + * @psalm-param 'GET'|'HEAD'|'POST'|'PUT'|'DELETE'|'OPTIONS'|'PATCH' $verb + * @param string $url The path of the route. + * @param ?array<string, string> $requirements Array of regexes mapped to the path parameters. + * @param ?array<string, mixed> $defaults Array of default values mapped to the path parameters. + * @param ?string $root Custom root. For OCS all apps are allowed, but for index.php only some can use it. + * @param ?string $postfix Postfix for the route name. + * @since 29.0.0 + */ + public function __construct( + protected string $type, + protected string $verb, + protected string $url, + protected ?array $requirements = null, + protected ?array $defaults = null, + protected ?string $root = null, + protected ?string $postfix = null, + ) { + } + + /** + * @return array{ + * verb: string, + * url: string, + * requirements?: array<string, string>, + * defaults?: array<string, mixed>, + * root?: string, + * postfix?: string, + * } + * @since 29.0.0 + */ + public function toArray() { + $route = [ + 'verb' => $this->verb, + 'url' => $this->url, + ]; + + if ($this->requirements !== null) { + $route['requirements'] = $this->requirements; + } + if ($this->defaults !== null) { + $route['defaults'] = $this->defaults; + } + if ($this->root !== null) { + $route['root'] = $this->root; + } + if ($this->postfix !== null) { + $route['postfix'] = $this->postfix; + } + + return $route; + } + + /** + * @since 29.0.0 + */ + public function getType(): string { + return $this->type; + } + + /** + * @since 29.0.0 + */ + public function getVerb(): string { + return $this->verb; + } + + /** + * @since 29.0.0 + */ + public function getUrl(): string { + return $this->url; + } + + /** + * @since 29.0.0 + */ + public function getRequirements(): ?array { + return $this->requirements; + } + + /** + * @since 29.0.0 + */ + public function getDefaults(): ?array { + return $this->defaults; + } + + /** + * @since 29.0.0 + */ + public function getRoot(): ?string { + return $this->root; + } + + /** + * @since 29.0.0 + */ + public function getPostfix(): ?string { + return $this->postfix; + } +} diff --git a/lib/public/EventDispatcher/IEventDispatcher.php b/lib/public/EventDispatcher/IEventDispatcher.php index 0a96fa799d4..a84e0fe2f3b 100644 --- a/lib/public/EventDispatcher/IEventDispatcher.php +++ b/lib/public/EventDispatcher/IEventDispatcher.php @@ -71,6 +71,15 @@ interface IEventDispatcher { /** * @template T of \OCP\EventDispatcher\Event + * @param string $eventName preferably the fully-qualified class name of the Event sub class + * + * @return bool TRUE if event has registered listeners + * @since 29.0.0 + */ + public function hasListeners(string $eventName): bool; + + /** + * @template T of \OCP\EventDispatcher\Event * @param string $eventName * @psalm-param string|class-string<T> $eventName * @param Event $event diff --git a/lib/public/FullTextSearch/Exceptions/FullTextSearchIndexNotAvailableException.php b/lib/public/FullTextSearch/Exceptions/FullTextSearchIndexNotAvailableException.php new file mode 100644 index 00000000000..cf3c5905135 --- /dev/null +++ b/lib/public/FullTextSearch/Exceptions/FullTextSearchIndexNotAvailableException.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2023, Faraz Samapoor <f.samapoor@gmail.com> + * + * @author Faraz Samapoor <f.samapoor@gmail.com> + * + * @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 OCP\FullTextSearch\Exceptions; + +/** + * @since 28.0.0 + * + * Class FullTextSearchIndexNotAvailableException + * + */ +class FullTextSearchIndexNotAvailableException extends \Exception { +} |