aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files/l10n/fr.js12
-rw-r--r--apps/files/l10n/fr.json12
-rw-r--r--apps/files_sharing/l10n/ca.js2
-rw-r--r--apps/files_sharing/l10n/ca.json2
-rw-r--r--apps/files_sharing/l10n/fr.js16
-rw-r--r--apps/files_sharing/l10n/fr.json16
-rw-r--r--apps/files_versions/l10n/eu.js4
-rw-r--r--apps/files_versions/l10n/eu.json4
-rw-r--r--apps/settings/l10n/ar.js2
-rw-r--r--apps/settings/l10n/ar.json2
-rw-r--r--apps/settings/l10n/fr.js11
-rw-r--r--apps/settings/l10n/fr.json11
-rw-r--r--apps/settings/l10n/gl.js2
-rw-r--r--apps/settings/l10n/gl.json2
-rw-r--r--apps/settings/lib/Controller/CheckSetupController.php57
-rw-r--r--apps/settings/tests/Controller/CheckSetupControllerTest.php11
-rw-r--r--apps/theming/css/default.css2
-rw-r--r--apps/theming/lib/Themes/CommonThemeTrait.php2
-rw-r--r--apps/twofactor_backupcodes/l10n/fa.js23
-rw-r--r--apps/twofactor_backupcodes/l10n/fa.json21
-rw-r--r--apps/workflowengine/l10n/fr.js3
-rw-r--r--apps/workflowengine/l10n/fr.json3
-rw-r--r--core/Application.php481
-rw-r--r--core/Command/Db/AddMissingColumns.php59
-rw-r--r--core/Command/Db/AddMissingIndices.php446
-rw-r--r--core/Command/Db/AddMissingPrimaryKeys.php142
-rw-r--r--core/l10n/ar.js2
-rw-r--r--core/l10n/ar.json2
-rw-r--r--core/l10n/gl.js2
-rw-r--r--core/l10n/gl.json2
-rw-r--r--core/register_command.php4
-rw-r--r--lib/composer/composer/autoload_classmap.php2
-rw-r--r--lib/composer/composer/autoload_static.php2
-rw-r--r--lib/l10n/gl.js3
-rw-r--r--lib/l10n/gl.json3
-rw-r--r--lib/public/DB/Events/AddMissingColumnsEvent.php60
-rw-r--r--lib/public/DB/Events/AddMissingIndicesEvent.php31
-rw-r--r--lib/public/DB/Events/AddMissingPrimaryKeyEvent.php60
-rw-r--r--lib/public/IDBConnection.php31
39 files changed, 642 insertions, 910 deletions
diff --git a/apps/files/l10n/fr.js b/apps/files/l10n/fr.js
index 3535cd28010..71123ffab14 100644
--- a/apps/files/l10n/fr.js
+++ b/apps/files/l10n/fr.js
@@ -171,11 +171,20 @@ OC.L10N.register(
"Reload current directory" : "Rafraîchir le dossier courant",
"Go to the \"{dir}\" directory" : "Aller au dossier \"{dir}\"",
"Select the row for {displayName}" : "Sélectionner la ligne pour {displayName}",
+ "Rename file" : "Renommer le fichier",
"File name" : "Nom du fichier",
+ "A long time ago" : "Il y a longtemps",
"Download file {name}" : "Télécharger le fichier {name}",
"\"{displayName}\" action executed successfully" : "Action \"{displayName}\" exécutée avec succès",
"\"{displayName}\" action failed" : "Échec de l'action \"{displayName}\"",
+ "\"{name}\" is not an allowed filetype." : "\"{name}\" n'est pas un type de fichier autorisé.",
+ "{newName} already exists." : "{newName} existe déjà.",
"Name cannot be empty" : "Le nom ne peut pas être vide",
+ "Another entry with the same name already exists" : "Une autre entrée avec le même nom existe déjà",
+ "Renamed \"{oldName}\" to \"{newName}\"" : "Renommer \"{oldName}\" en \"{newName}\"",
+ "Could not rename \"{oldName}\", it does not exist any more" : "Impossible de renommer \"{oldName}\", il n'existe plus.",
+ "The name \"{newName}\"\" is already used in the folder \"{dir}\". Please choose a different name." : "Le nom \"{newName}\"\" est déjà utilisé dans le dossier \"{dir}\". Merci de choisir un autre nom.",
+ "Could not rename \"{oldName}\"" : "Impossible de renommer \"{oldName}\"",
"Total rows summary" : "Récapitulatif du nombre total de lignes",
"Select all" : "Tout sélectionner",
"Unselect all" : "Tout désélectionner",
@@ -184,6 +193,7 @@ OC.L10N.register(
"ascending" : "ascendant",
"descending" : "descendant",
"Sort list by {column} ({direction})" : "Trier la liste par {column} ({direction})",
+ "List of files and folders." : "Liste des fichiers et dossiers.",
"This list is not fully rendered for performances reasons. The files will be rendered as you navigate through the list." : "Cette liste n'est pas affichée en totalité pour des raisons de performances. Les fichiers seront affichés au fur et à mesure que vous parcourrez la liste.",
"Storage informations" : "Informations sur le stockage",
"{usedQuotaByte} used" : "{usedQuotaByte} utilisés",
@@ -210,6 +220,7 @@ OC.L10N.register(
"Files settings" : "Paramètres des fichiers",
"File cannot be accessed" : "Impossible d'accéder au fichier",
"You might not have have permissions to view it, ask the sender to share it" : "Vous n’avez peut-être pas les autorisations pour le voir, demandez à l’expéditeur de le partager",
+ "Sort favorites first" : "Trier les favoris d'abord",
"Show hidden files" : "Afficher les fichiers masqués",
"Crop image previews" : "Afficher en miniatures carrées",
"Additional settings" : "Paramètres supplémentaires",
@@ -234,6 +245,7 @@ OC.L10N.register(
"Templates" : "Modèles",
"Create new templates folder" : "Créer un nouveau dossier de modèles",
"Unable to initialize the templates directory" : "Impossible d'initialiser le dossier des modèles",
+ "List of favorites files and folders." : "Liste des fichiers et dossiers favoris.",
"No favorites yet" : "Aucun favori pour l'instant",
"Files and folders you mark as favorite will show up here" : "Les fichiers et dossiers ajoutés à vos favoris apparaîtront ici",
"Toggle %1$s sublist" : "Basculer %1$s sous-liste",
diff --git a/apps/files/l10n/fr.json b/apps/files/l10n/fr.json
index 269c26dbee7..568183c64ff 100644
--- a/apps/files/l10n/fr.json
+++ b/apps/files/l10n/fr.json
@@ -169,11 +169,20 @@
"Reload current directory" : "Rafraîchir le dossier courant",
"Go to the \"{dir}\" directory" : "Aller au dossier \"{dir}\"",
"Select the row for {displayName}" : "Sélectionner la ligne pour {displayName}",
+ "Rename file" : "Renommer le fichier",
"File name" : "Nom du fichier",
+ "A long time ago" : "Il y a longtemps",
"Download file {name}" : "Télécharger le fichier {name}",
"\"{displayName}\" action executed successfully" : "Action \"{displayName}\" exécutée avec succès",
"\"{displayName}\" action failed" : "Échec de l'action \"{displayName}\"",
+ "\"{name}\" is not an allowed filetype." : "\"{name}\" n'est pas un type de fichier autorisé.",
+ "{newName} already exists." : "{newName} existe déjà.",
"Name cannot be empty" : "Le nom ne peut pas être vide",
+ "Another entry with the same name already exists" : "Une autre entrée avec le même nom existe déjà",
+ "Renamed \"{oldName}\" to \"{newName}\"" : "Renommer \"{oldName}\" en \"{newName}\"",
+ "Could not rename \"{oldName}\", it does not exist any more" : "Impossible de renommer \"{oldName}\", il n'existe plus.",
+ "The name \"{newName}\"\" is already used in the folder \"{dir}\". Please choose a different name." : "Le nom \"{newName}\"\" est déjà utilisé dans le dossier \"{dir}\". Merci de choisir un autre nom.",
+ "Could not rename \"{oldName}\"" : "Impossible de renommer \"{oldName}\"",
"Total rows summary" : "Récapitulatif du nombre total de lignes",
"Select all" : "Tout sélectionner",
"Unselect all" : "Tout désélectionner",
@@ -182,6 +191,7 @@
"ascending" : "ascendant",
"descending" : "descendant",
"Sort list by {column} ({direction})" : "Trier la liste par {column} ({direction})",
+ "List of files and folders." : "Liste des fichiers et dossiers.",
"This list is not fully rendered for performances reasons. The files will be rendered as you navigate through the list." : "Cette liste n'est pas affichée en totalité pour des raisons de performances. Les fichiers seront affichés au fur et à mesure que vous parcourrez la liste.",
"Storage informations" : "Informations sur le stockage",
"{usedQuotaByte} used" : "{usedQuotaByte} utilisés",
@@ -208,6 +218,7 @@
"Files settings" : "Paramètres des fichiers",
"File cannot be accessed" : "Impossible d'accéder au fichier",
"You might not have have permissions to view it, ask the sender to share it" : "Vous n’avez peut-être pas les autorisations pour le voir, demandez à l’expéditeur de le partager",
+ "Sort favorites first" : "Trier les favoris d'abord",
"Show hidden files" : "Afficher les fichiers masqués",
"Crop image previews" : "Afficher en miniatures carrées",
"Additional settings" : "Paramètres supplémentaires",
@@ -232,6 +243,7 @@
"Templates" : "Modèles",
"Create new templates folder" : "Créer un nouveau dossier de modèles",
"Unable to initialize the templates directory" : "Impossible d'initialiser le dossier des modèles",
+ "List of favorites files and folders." : "Liste des fichiers et dossiers favoris.",
"No favorites yet" : "Aucun favori pour l'instant",
"Files and folders you mark as favorite will show up here" : "Les fichiers et dossiers ajoutés à vos favoris apparaîtront ici",
"Toggle %1$s sublist" : "Basculer %1$s sous-liste",
diff --git a/apps/files_sharing/l10n/ca.js b/apps/files_sharing/l10n/ca.js
index ad0a035084f..7c8b539d5c2 100644
--- a/apps/files_sharing/l10n/ca.js
+++ b/apps/files_sharing/l10n/ca.js
@@ -232,7 +232,7 @@ OC.L10N.register(
"Shared with you and {circle} by {owner}" : "{owner} l'ha compartit amb vós i amb el cercle {circle}",
"Shared with you and the conversation {conversation} by {owner}" : "{owner} l'ha compartit amb vós i amb la conversa {conversation}",
"Shared with you in a conversation by {owner}" : "{owner} l'ha compartit amb vós en una conversa",
- "Shares" : "Element compartits",
+ "Shares" : "Elements compartits",
"Overview of shared files." : "Informació general dels fitxers compartits.",
"No shares" : "No hi ha cap element compartit",
"Files and folders you shared or have been shared with you will show up here" : "Els fitxers i les carpetes que compartiu o s'hagin compartit amb vós es mostraran aquí",
diff --git a/apps/files_sharing/l10n/ca.json b/apps/files_sharing/l10n/ca.json
index ec4a22e8ae1..a67ff3b2481 100644
--- a/apps/files_sharing/l10n/ca.json
+++ b/apps/files_sharing/l10n/ca.json
@@ -230,7 +230,7 @@
"Shared with you and {circle} by {owner}" : "{owner} l'ha compartit amb vós i amb el cercle {circle}",
"Shared with you and the conversation {conversation} by {owner}" : "{owner} l'ha compartit amb vós i amb la conversa {conversation}",
"Shared with you in a conversation by {owner}" : "{owner} l'ha compartit amb vós en una conversa",
- "Shares" : "Element compartits",
+ "Shares" : "Elements compartits",
"Overview of shared files." : "Informació general dels fitxers compartits.",
"No shares" : "No hi ha cap element compartit",
"Files and folders you shared or have been shared with you will show up here" : "Els fitxers i les carpetes que compartiu o s'hagin compartit amb vós es mostraran aquí",
diff --git a/apps/files_sharing/l10n/fr.js b/apps/files_sharing/l10n/fr.js
index 316ec6379b1..9a4953e9766 100644
--- a/apps/files_sharing/l10n/fr.js
+++ b/apps/files_sharing/l10n/fr.js
@@ -210,6 +210,10 @@ OC.L10N.register(
"Expires {relativetime}" : "Expire {relativetime}",
"this share just expired." : "ce partage vient d'expirer",
"Shared with you by {owner}" : "Partagé avec vous par {owner}",
+ "_Accept share_::_Accept shares_" : ["Accepter le partage","Accepter les partages","Accepter les partages"],
+ "Open in files" : "Ouvrir dans Fichiers",
+ "_Reject share_::_Reject shares_" : ["Refuser le partage","Refuser les partages","Refuser les partages"],
+ "_Restore share_::_Restore shares_" : ["Restaurer le partage","Restaurer les partages","Restaurer les partages"],
"Link to a file" : "Relier à un fichier",
"Error creating the share: {errorMessage}" : "Erreur à la création du partage : {errorMessage} ",
"Error creating the share" : "Erreur lors de la création du partage",
@@ -229,17 +233,29 @@ OC.L10N.register(
"Shared with you and the conversation {conversation} by {owner}" : "Partagé avec vous et la conversation {conversation} par {owner}",
"Shared with you in a conversation by {owner}" : "Partagé avec vous dans une conversation de {owner}",
"Shares" : "Partages",
+ "Overview of shared files." : "Aperçu des fichiers partagés.",
"No shares" : "Aucun partage",
+ "Files and folders you shared or have been shared with you will show up here" : "Les fichiers et les dossiers que vous avez partagés ou qui vous ont été partagés apparaîtront ici",
"Shared with you" : "Partagés avec vous",
+ "List of files that are shared with you." : "Liste des fichiers qui sont partagés avec vous.",
"Nothing shared with you yet" : "Aucun fichier n'est partagé avec vous pour l'instant",
+ "Files and folders others shared with you will show up here" : "Les fichiers et les dossiers que les autres ont partagés avec vous apparaîtront ici",
"Shared with others" : "Partagés avec d'autres",
+ "List of files that you shared with others." : "Liste des fichiers que vous avez partagés avec d'autres",
"Nothing shared yet" : "Rien n'est partagé pour l'instant",
+ "Files and folders you shared will show up here" : "Les fichiers et les dossiers que vous avez partagés apparaîtront ici",
"Shared by link" : "Partagés par lien",
+ "List of files that are shared by link." : "Liste des fichiers qui sont partagés par lien.",
"No shared links" : "Aucun partage par lien",
+ "Files and folders you shared by link will show up here" : "Les fichiers et les dossiers que vous avez partagés par lien apparaîtront ici",
"Deleted shares" : "Partages supprimés",
+ "List of shares you left." : "Liste des partages auxquels vous avez mis fin.",
"No deleted shares" : "Aucun partage supprimé",
+ "Shares you have left will show up here" : "Les partages auxquels vous avez mis fin apparaîtront ici",
"Pending shares" : "Partages en attente",
+ "List of unapproved shares." : "Liste des partages non approuvés.",
"No pending shares" : "Aucun partage en attente",
+ "Shares you have received but not approved will show up here" : "Les partages que vous avez reçus mais non approuvés apparaîtront ici.",
"No entries found in this folder" : "Aucune entrée trouvée dans ce dossier",
"Name" : "Nom",
"Share time" : "Date de partage",
diff --git a/apps/files_sharing/l10n/fr.json b/apps/files_sharing/l10n/fr.json
index ef8926955ed..f7aa987402f 100644
--- a/apps/files_sharing/l10n/fr.json
+++ b/apps/files_sharing/l10n/fr.json
@@ -208,6 +208,10 @@
"Expires {relativetime}" : "Expire {relativetime}",
"this share just expired." : "ce partage vient d'expirer",
"Shared with you by {owner}" : "Partagé avec vous par {owner}",
+ "_Accept share_::_Accept shares_" : ["Accepter le partage","Accepter les partages","Accepter les partages"],
+ "Open in files" : "Ouvrir dans Fichiers",
+ "_Reject share_::_Reject shares_" : ["Refuser le partage","Refuser les partages","Refuser les partages"],
+ "_Restore share_::_Restore shares_" : ["Restaurer le partage","Restaurer les partages","Restaurer les partages"],
"Link to a file" : "Relier à un fichier",
"Error creating the share: {errorMessage}" : "Erreur à la création du partage : {errorMessage} ",
"Error creating the share" : "Erreur lors de la création du partage",
@@ -227,17 +231,29 @@
"Shared with you and the conversation {conversation} by {owner}" : "Partagé avec vous et la conversation {conversation} par {owner}",
"Shared with you in a conversation by {owner}" : "Partagé avec vous dans une conversation de {owner}",
"Shares" : "Partages",
+ "Overview of shared files." : "Aperçu des fichiers partagés.",
"No shares" : "Aucun partage",
+ "Files and folders you shared or have been shared with you will show up here" : "Les fichiers et les dossiers que vous avez partagés ou qui vous ont été partagés apparaîtront ici",
"Shared with you" : "Partagés avec vous",
+ "List of files that are shared with you." : "Liste des fichiers qui sont partagés avec vous.",
"Nothing shared with you yet" : "Aucun fichier n'est partagé avec vous pour l'instant",
+ "Files and folders others shared with you will show up here" : "Les fichiers et les dossiers que les autres ont partagés avec vous apparaîtront ici",
"Shared with others" : "Partagés avec d'autres",
+ "List of files that you shared with others." : "Liste des fichiers que vous avez partagés avec d'autres",
"Nothing shared yet" : "Rien n'est partagé pour l'instant",
+ "Files and folders you shared will show up here" : "Les fichiers et les dossiers que vous avez partagés apparaîtront ici",
"Shared by link" : "Partagés par lien",
+ "List of files that are shared by link." : "Liste des fichiers qui sont partagés par lien.",
"No shared links" : "Aucun partage par lien",
+ "Files and folders you shared by link will show up here" : "Les fichiers et les dossiers que vous avez partagés par lien apparaîtront ici",
"Deleted shares" : "Partages supprimés",
+ "List of shares you left." : "Liste des partages auxquels vous avez mis fin.",
"No deleted shares" : "Aucun partage supprimé",
+ "Shares you have left will show up here" : "Les partages auxquels vous avez mis fin apparaîtront ici",
"Pending shares" : "Partages en attente",
+ "List of unapproved shares." : "Liste des partages non approuvés.",
"No pending shares" : "Aucun partage en attente",
+ "Shares you have received but not approved will show up here" : "Les partages que vous avez reçus mais non approuvés apparaîtront ici.",
"No entries found in this folder" : "Aucune entrée trouvée dans ce dossier",
"Name" : "Nom",
"Share time" : "Date de partage",
diff --git a/apps/files_versions/l10n/eu.js b/apps/files_versions/l10n/eu.js
index 1ce27ba7b73..5777fba70bf 100644
--- a/apps/files_versions/l10n/eu.js
+++ b/apps/files_versions/l10n/eu.js
@@ -4,7 +4,7 @@ OC.L10N.register(
"Versions" : "Bertsioak",
"This application automatically maintains older versions of files that are changed." : "Aplikazio honek aldatzen diren fitxategien bertsio zaharrak mantentzen ditu automatikoki.",
"This application automatically maintains older versions of files that are changed. When enabled, a hidden versions folder is provisioned in every user's directory and is used to store old file versions. A user can revert to an older version through the web interface at any time, with the replaced file becoming a version. The app automatically manages the versions folder to ensure the user does not run out of Quota because of versions.\n\t\tIn addition to the expiry of versions, the versions app makes certain never to use more than 50% of the user's currently available free space. If stored versions exceed this limit, the app will delete the oldest versions first until it meets this limit. More information is available in the Versions documentation." : "Aplikazio honek automatikoki mantentzen ditu aldatzen diren fitxategien bertsio zaharragoak. Gaituta dagoenean, ezkutuko bertsioen karpeta batez hornitzen da erabiltzaile bakoitzaren direktorioa, fitxategien bertsio zaharrak gordetzeko. Erabiltzaileak edozein unetan bertsio zaharrago batera leheneratu dezake web interfazearen bidez, ordeztutako fitxategia bertsio bihurtuz. Aplikazioak automatikoki kudeatzen du bertsioen karpeta, erabiltzailea bertsioak direla eta kuotarik gabe geratuko ez dela ziurtatzeko.\n\t\tBertsioak iraungitzeaz gain, bertsioen aplikazioak ziurtatzen du ez dela inoiz erabiliko erabiltzailearen uneko espazio librearen %50 baino gehiago. Biltegiratutako bertsioek muga hori gainditzen badute, aplikazioak bertsio zaharrenak ezabatuko ditu, mugara jaitsi arte. Informazio gehiago dago eskuragarri bertsioen dokumentazioan.",
- "Name this version" : "Izena eman bertsio honi",
+ "Name this version" : "Eman izena bertsio honi",
"Edit version name" : "Editatu bertsioaren izena",
"Restore version" : "Leheneratu bertsioa",
"Download version" : "Deskargatu bertsioa",
@@ -13,7 +13,7 @@ OC.L10N.register(
"Named versions are persisted, and excluded from automatic cleanups when your storage quota is full." : "Izendatutako bertsioak mantentzen dira eta garbiketa automatikoetatik kanpo geratzen dira biltegiratze-kuota beteta dagoenean.",
"Remove version name" : "Kendu bertsioaren izena",
"Save version name" : "Gorde bertsioaren izena",
- "Initial version restored" : "Hasierako bertsioa leheneratua",
+ "Initial version restored" : "Hasierako bertsioa ondo leheneratu da",
"Version restored" : "Bertsioa leheneratu da",
"Could not restore version" : "Ezin izan da bertsioa leheneratu",
"Could not set version name" : "Ezin izan da bertsioaren izena ezarri",
diff --git a/apps/files_versions/l10n/eu.json b/apps/files_versions/l10n/eu.json
index 4e03d6de44e..737ebc9bef4 100644
--- a/apps/files_versions/l10n/eu.json
+++ b/apps/files_versions/l10n/eu.json
@@ -2,7 +2,7 @@
"Versions" : "Bertsioak",
"This application automatically maintains older versions of files that are changed." : "Aplikazio honek aldatzen diren fitxategien bertsio zaharrak mantentzen ditu automatikoki.",
"This application automatically maintains older versions of files that are changed. When enabled, a hidden versions folder is provisioned in every user's directory and is used to store old file versions. A user can revert to an older version through the web interface at any time, with the replaced file becoming a version. The app automatically manages the versions folder to ensure the user does not run out of Quota because of versions.\n\t\tIn addition to the expiry of versions, the versions app makes certain never to use more than 50% of the user's currently available free space. If stored versions exceed this limit, the app will delete the oldest versions first until it meets this limit. More information is available in the Versions documentation." : "Aplikazio honek automatikoki mantentzen ditu aldatzen diren fitxategien bertsio zaharragoak. Gaituta dagoenean, ezkutuko bertsioen karpeta batez hornitzen da erabiltzaile bakoitzaren direktorioa, fitxategien bertsio zaharrak gordetzeko. Erabiltzaileak edozein unetan bertsio zaharrago batera leheneratu dezake web interfazearen bidez, ordeztutako fitxategia bertsio bihurtuz. Aplikazioak automatikoki kudeatzen du bertsioen karpeta, erabiltzailea bertsioak direla eta kuotarik gabe geratuko ez dela ziurtatzeko.\n\t\tBertsioak iraungitzeaz gain, bertsioen aplikazioak ziurtatzen du ez dela inoiz erabiliko erabiltzailearen uneko espazio librearen %50 baino gehiago. Biltegiratutako bertsioek muga hori gainditzen badute, aplikazioak bertsio zaharrenak ezabatuko ditu, mugara jaitsi arte. Informazio gehiago dago eskuragarri bertsioen dokumentazioan.",
- "Name this version" : "Izena eman bertsio honi",
+ "Name this version" : "Eman izena bertsio honi",
"Edit version name" : "Editatu bertsioaren izena",
"Restore version" : "Leheneratu bertsioa",
"Download version" : "Deskargatu bertsioa",
@@ -11,7 +11,7 @@
"Named versions are persisted, and excluded from automatic cleanups when your storage quota is full." : "Izendatutako bertsioak mantentzen dira eta garbiketa automatikoetatik kanpo geratzen dira biltegiratze-kuota beteta dagoenean.",
"Remove version name" : "Kendu bertsioaren izena",
"Save version name" : "Gorde bertsioaren izena",
- "Initial version restored" : "Hasierako bertsioa leheneratua",
+ "Initial version restored" : "Hasierako bertsioa ondo leheneratu da",
"Version restored" : "Bertsioa leheneratu da",
"Could not restore version" : "Ezin izan da bertsioa leheneratu",
"Could not set version name" : "Ezin izan da bertsioaren izena ezarri",
diff --git a/apps/settings/l10n/ar.js b/apps/settings/l10n/ar.js
index 99600a99a1b..75acff174ff 100644
--- a/apps/settings/l10n/ar.js
+++ b/apps/settings/l10n/ar.js
@@ -361,6 +361,8 @@ OC.L10N.register(
"Username (required)" : "اسم المستخدم (لازم)",
"Total rows summary" : "ملخص مجموع الأسطر",
"Scroll to load more rows" : "مرِّر على القائمة لتحميل المزيد من الصفوف",
+ "_{userCount} user …_::_{userCount} users …_" : ["{userCount} مستخدم …","{userCount} مستخدِم …","{userCount} مستخدِمان …","{userCount} مستخدِم …","{userCount} مستخدِم …","{userCount} users …"],
+ "_{userCount} user_::_{userCount} users_" : ["{userCount} مستخدِم","{userCount} مستخدِم","{userCount} مستخدِم","{userCount} مستخدِم","{userCount} مستخدِم","{userCount} مستخدِم"],
"Avatar" : "رمز تجسيدي avatar",
"Group admin for" : "مشرف المجموعة Group admin لـ",
"User backend" : "الواجهة الخلفية للمستخدم",
diff --git a/apps/settings/l10n/ar.json b/apps/settings/l10n/ar.json
index 692a567ea53..267dd3f14dc 100644
--- a/apps/settings/l10n/ar.json
+++ b/apps/settings/l10n/ar.json
@@ -359,6 +359,8 @@
"Username (required)" : "اسم المستخدم (لازم)",
"Total rows summary" : "ملخص مجموع الأسطر",
"Scroll to load more rows" : "مرِّر على القائمة لتحميل المزيد من الصفوف",
+ "_{userCount} user …_::_{userCount} users …_" : ["{userCount} مستخدم …","{userCount} مستخدِم …","{userCount} مستخدِمان …","{userCount} مستخدِم …","{userCount} مستخدِم …","{userCount} users …"],
+ "_{userCount} user_::_{userCount} users_" : ["{userCount} مستخدِم","{userCount} مستخدِم","{userCount} مستخدِم","{userCount} مستخدِم","{userCount} مستخدِم","{userCount} مستخدِم"],
"Avatar" : "رمز تجسيدي avatar",
"Group admin for" : "مشرف المجموعة Group admin لـ",
"User backend" : "الواجهة الخلفية للمستخدم",
diff --git a/apps/settings/l10n/fr.js b/apps/settings/l10n/fr.js
index 42609ea2120..1813fc87473 100644
--- a/apps/settings/l10n/fr.js
+++ b/apps/settings/l10n/fr.js
@@ -334,6 +334,9 @@ OC.L10N.register(
"Unable to update federation scope of additional {property}" : "Impossible de mettre à jour la portée de la fédération pour le/la {property} additionnel(le)",
"Add additional email" : "Ajouter un e-mail supplémentaire",
"Add" : "Ajouter",
+ "No users" : "Pas d'utilisateurs",
+ "Loading users …" : "Chargement des utilisateurs...",
+ "List of users. This list is not fully rendered for performances reasons. The users will be rendered as you navigate through the list." : "Liste des utilisateurs. Cette liste n'est pas totalement affichée pour des raisons de performances. Les utilisateurs seront affichés au fur et à mesure que vous naviguez dans la liste.",
"Default language" : "Langue par défaut",
"Common languages" : "Langues communes",
"Other languages" : "Autres langues",
@@ -357,12 +360,16 @@ OC.L10N.register(
"Username will be autogenerated" : "Le nom d'utilisateur sera généré automatiquement",
"Username (required)" : "Nom d'utilisateur (requis)",
"Total rows summary" : "Récapitulatif du nombre total de lignes",
+ "Scroll to load more rows" : "Défiler pour charger plus de lignes",
+ "_{userCount} user …_::_{userCount} users …_" : ["{userCount} utilisateur …","{userCount} utilisateurs …","{userCount} utilisateurs …"],
+ "_{userCount} user_::_{userCount} users_" : ["{userCount} utilisateur","{userCount} utilisateurs","{userCount} utilisateurs"],
"Avatar" : "Avatar",
"Group admin for" : "Administrateur de groupe pour",
"User backend" : "Origine du compte",
"Storage location" : "Emplacement du stockage",
"Last login" : "Dernière connexion",
"User actions" : "Actions de l'utilisateur",
+ "Loading user …" : "Chargement de l'utilisateur...",
"Edit display name" : "Modifier le nom d'affichage",
"Add new password" : "Ajouter un nouveau mot de passe",
"You do not have permissions to see the details of this user" : "Vous n'avez pas les autorisations pour voir le détail de cet utilisateur",
@@ -381,6 +388,7 @@ OC.L10N.register(
"Remote wipe of devices" : "Effacer les appareils à distance",
"Wipe {userid}'s devices" : "Effacer les appareils de {userid}",
"Wiped {userid}'s devices" : "L'appareil de {userid}'s a été effacé",
+ "Failed to update user manager" : "Impossible de mettre à jour le gestionnaire d'utilisateurs",
"Fully delete {userid}'s account including all their personal files, app data, etc." : "Supprime totalement le compte de {userid} et toutes ses données associées (fichiers personnels, données des applications, etc.)",
"Account deletion" : "Suppression de compte",
"Delete {userid}'s account" : "Supprimer le compte {userid}",
@@ -390,11 +398,14 @@ OC.L10N.register(
"Welcome mail sent!" : "E-mail de bienvenue envoyé !",
"Toggle user actions menu" : "Afficher / Masquer le menu des actions de l'utilisateur",
"Edit" : "Editer",
+ "User management settings" : "Paramètres de la gestion utilisateurs",
"Visibility" : "Visibilité",
+ "Show language" : "Montrer la langue utilisée",
"Show user backend" : "Afficher l'origine du compte",
"Show storage path" : "Afficher l'emplacement du stockage",
"Show last login" : "Afficher la dernière connexion",
"Send email" : "Envoyer un e-mail",
+ "Send welcome email to new users" : "Envoyer un mail de bienvenue aux nouveaux utilisateurs",
"Defaults" : "Par défaut",
"Default quota" : "Quota par défaut",
"Select default quota" : "Sélectionner le quota par défaut",
diff --git a/apps/settings/l10n/fr.json b/apps/settings/l10n/fr.json
index f97a7634e14..c7d2e810317 100644
--- a/apps/settings/l10n/fr.json
+++ b/apps/settings/l10n/fr.json
@@ -332,6 +332,9 @@
"Unable to update federation scope of additional {property}" : "Impossible de mettre à jour la portée de la fédération pour le/la {property} additionnel(le)",
"Add additional email" : "Ajouter un e-mail supplémentaire",
"Add" : "Ajouter",
+ "No users" : "Pas d'utilisateurs",
+ "Loading users …" : "Chargement des utilisateurs...",
+ "List of users. This list is not fully rendered for performances reasons. The users will be rendered as you navigate through the list." : "Liste des utilisateurs. Cette liste n'est pas totalement affichée pour des raisons de performances. Les utilisateurs seront affichés au fur et à mesure que vous naviguez dans la liste.",
"Default language" : "Langue par défaut",
"Common languages" : "Langues communes",
"Other languages" : "Autres langues",
@@ -355,12 +358,16 @@
"Username will be autogenerated" : "Le nom d'utilisateur sera généré automatiquement",
"Username (required)" : "Nom d'utilisateur (requis)",
"Total rows summary" : "Récapitulatif du nombre total de lignes",
+ "Scroll to load more rows" : "Défiler pour charger plus de lignes",
+ "_{userCount} user …_::_{userCount} users …_" : ["{userCount} utilisateur …","{userCount} utilisateurs …","{userCount} utilisateurs …"],
+ "_{userCount} user_::_{userCount} users_" : ["{userCount} utilisateur","{userCount} utilisateurs","{userCount} utilisateurs"],
"Avatar" : "Avatar",
"Group admin for" : "Administrateur de groupe pour",
"User backend" : "Origine du compte",
"Storage location" : "Emplacement du stockage",
"Last login" : "Dernière connexion",
"User actions" : "Actions de l'utilisateur",
+ "Loading user …" : "Chargement de l'utilisateur...",
"Edit display name" : "Modifier le nom d'affichage",
"Add new password" : "Ajouter un nouveau mot de passe",
"You do not have permissions to see the details of this user" : "Vous n'avez pas les autorisations pour voir le détail de cet utilisateur",
@@ -379,6 +386,7 @@
"Remote wipe of devices" : "Effacer les appareils à distance",
"Wipe {userid}'s devices" : "Effacer les appareils de {userid}",
"Wiped {userid}'s devices" : "L'appareil de {userid}'s a été effacé",
+ "Failed to update user manager" : "Impossible de mettre à jour le gestionnaire d'utilisateurs",
"Fully delete {userid}'s account including all their personal files, app data, etc." : "Supprime totalement le compte de {userid} et toutes ses données associées (fichiers personnels, données des applications, etc.)",
"Account deletion" : "Suppression de compte",
"Delete {userid}'s account" : "Supprimer le compte {userid}",
@@ -388,11 +396,14 @@
"Welcome mail sent!" : "E-mail de bienvenue envoyé !",
"Toggle user actions menu" : "Afficher / Masquer le menu des actions de l'utilisateur",
"Edit" : "Editer",
+ "User management settings" : "Paramètres de la gestion utilisateurs",
"Visibility" : "Visibilité",
+ "Show language" : "Montrer la langue utilisée",
"Show user backend" : "Afficher l'origine du compte",
"Show storage path" : "Afficher l'emplacement du stockage",
"Show last login" : "Afficher la dernière connexion",
"Send email" : "Envoyer un e-mail",
+ "Send welcome email to new users" : "Envoyer un mail de bienvenue aux nouveaux utilisateurs",
"Defaults" : "Par défaut",
"Default quota" : "Quota par défaut",
"Select default quota" : "Sélectionner le quota par défaut",
diff --git a/apps/settings/l10n/gl.js b/apps/settings/l10n/gl.js
index bf7b2eb5686..916a54a1232 100644
--- a/apps/settings/l10n/gl.js
+++ b/apps/settings/l10n/gl.js
@@ -271,7 +271,7 @@ OC.L10N.register(
"Your profile information" : "A información do seu perfil",
"Your profile picture" : "A súa imaxe de perfil",
"Upload profile picture" : "Enviar a imaxe de perfil",
- "Choose profile picture from Files" : "Escolla a imaxe de perfil en Ficheiros",
+ "Choose profile picture from Files" : "Escoller a imaxe de perfil en Ficheiros",
"Remove profile picture" : "Retirar a imaxe de perfil",
"The file must be a PNG or JPG" : "O ficheiro debe ser PNG ou JPG",
"Picture provided by original account" : "Imaxe fornecida pola conta orixinal ",
diff --git a/apps/settings/l10n/gl.json b/apps/settings/l10n/gl.json
index 84cf04226f2..53680cc31c2 100644
--- a/apps/settings/l10n/gl.json
+++ b/apps/settings/l10n/gl.json
@@ -269,7 +269,7 @@
"Your profile information" : "A información do seu perfil",
"Your profile picture" : "A súa imaxe de perfil",
"Upload profile picture" : "Enviar a imaxe de perfil",
- "Choose profile picture from Files" : "Escolla a imaxe de perfil en Ficheiros",
+ "Choose profile picture from Files" : "Escoller a imaxe de perfil en Ficheiros",
"Remove profile picture" : "Retirar a imaxe de perfil",
"The file must be a PNG or JPG" : "O ficheiro debe ser PNG ou JPG",
"Picture provided by original account" : "Imaxe fornecida pola conta orixinal ",
diff --git a/apps/settings/lib/Controller/CheckSetupController.php b/apps/settings/lib/Controller/CheckSetupController.php
index 07fb627dbd8..4a1913cedfe 100644
--- a/apps/settings/lib/Controller/CheckSetupController.php
+++ b/apps/settings/lib/Controller/CheckSetupController.php
@@ -74,7 +74,9 @@ use OCP\AppFramework\Http\Attribute\IgnoreOpenAPI;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\RedirectResponse;
+use OCP\DB\Events\AddMissingColumnsEvent;
use OCP\DB\Events\AddMissingIndicesEvent;
+use OCP\DB\Events\AddMissingPrimaryKeyEvent;
use OCP\DB\Types;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Http\Client\IClientService;
@@ -90,8 +92,6 @@ use OCP\Lock\ILockingProvider;
use OCP\Notification\IManager;
use OCP\Security\ISecureRandom;
use Psr\Log\LoggerInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\GenericEvent;
#[IgnoreOpenAPI]
class CheckSetupController extends Controller {
@@ -108,8 +108,6 @@ class CheckSetupController extends Controller {
/** @var LoggerInterface */
private $logger;
/** @var IEventDispatcher */
- private $eventDispatcher;
- /** @var EventDispatcherInterface */
private $dispatcher;
/** @var Connection */
private $db;
@@ -142,8 +140,7 @@ class CheckSetupController extends Controller {
IL10N $l10n,
Checker $checker,
LoggerInterface $logger,
- IEventDispatcher $eventDispatcher,
- EventDispatcherInterface $dispatcher,
+ IEventDispatcher $dispatcher,
Connection $db,
ILockingProvider $lockingProvider,
IDateTimeFormatter $dateTimeFormatter,
@@ -163,7 +160,6 @@ class CheckSetupController extends Controller {
$this->l10n = $l10n;
$this->checker = $checker;
$this->logger = $logger;
- $this->eventDispatcher = $eventDispatcher;
$this->dispatcher = $dispatcher;
$this->db = $db;
$this->lockingProvider = $lockingProvider;
@@ -551,11 +547,8 @@ Raw output
$indexInfo = new MissingIndexInformation();
// Dispatch event so apps can also hint for pending index updates if needed
- $event = new GenericEvent($indexInfo);
- $this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);
-
$event = new AddMissingIndicesEvent();
- $this->eventDispatcher->dispatchTyped($event);
+ $this->dispatcher->dispatchTyped($event);
$missingIndices = $event->getMissingIndices();
if ($missingIndices !== []) {
@@ -575,20 +568,46 @@ Raw output
protected function hasMissingPrimaryKeys(): array {
$info = new MissingPrimaryKeyInformation();
- // Dispatch event so apps can also hint for pending index updates if needed
- $event = new GenericEvent($info);
- $this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_PRIMARY_KEYS_EVENT, $event);
+ // Dispatch event so apps can also hint for pending key updates if needed
+ $event = new AddMissingPrimaryKeyEvent();
+ $this->dispatcher->dispatchTyped($event);
+ $missingKeys = $event->getMissingPrimaryKeys();
+
+ if (!empty($missingKeys)) {
+ $schema = new SchemaWrapper(\OCP\Server::get(Connection::class));
+ foreach ($missingKeys as $missingKey) {
+ if ($schema->hasTable($missingKey['tableName'])) {
+ $table = $schema->getTable($missingKey['tableName']);
+ if (!$table->hasPrimaryKey()) {
+ $info->addHintForMissingSubject($missingKey['tableName']);
+ }
+ }
+ }
+ }
return $info->getListOfMissingPrimaryKeys();
}
protected function hasMissingColumns(): array {
- $indexInfo = new MissingColumnInformation();
- // Dispatch event so apps can also hint for pending index updates if needed
- $event = new GenericEvent($indexInfo);
- $this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_COLUMNS_EVENT, $event);
+ $columnInfo = new MissingColumnInformation();
+ // Dispatch event so apps can also hint for pending column updates if needed
+ $event = new AddMissingColumnsEvent();
+ $this->dispatcher->dispatchTyped($event);
+ $missingColumns = $event->getMissingColumns();
+
+ if (!empty($missingColumns)) {
+ $schema = new SchemaWrapper(\OCP\Server::get(Connection::class));
+ foreach ($missingColumns as $missingColumn) {
+ if ($schema->hasTable($missingColumn['tableName'])) {
+ $table = $schema->getTable($missingColumn['tableName']);
+ if (!$table->hasColumn($missingColumn['columnName'])) {
+ $columnInfo->addHintForMissingColumn($missingColumn['tableName'], $missingColumn['columnName']);
+ }
+ }
+ }
+ }
- return $indexInfo->getListOfMissingColumns();
+ return $columnInfo->getListOfMissingColumns();
}
protected function isSqliteUsed() {
diff --git a/apps/settings/tests/Controller/CheckSetupControllerTest.php b/apps/settings/tests/Controller/CheckSetupControllerTest.php
index 3fc4f930321..564c1cbb62d 100644
--- a/apps/settings/tests/Controller/CheckSetupControllerTest.php
+++ b/apps/settings/tests/Controller/CheckSetupControllerTest.php
@@ -62,7 +62,6 @@ use OCP\Notification\IManager;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Test\TestCase;
/**
@@ -89,8 +88,6 @@ class CheckSetupControllerTest extends TestCase {
/** @var Checker|\PHPUnit\Framework\MockObject\MockObject */
private $checker;
/** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */
- private $eventDispatcher;
- /** @var EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject */
private $dispatcher;
/** @var Connection|\PHPUnit\Framework\MockObject\MockObject */
private $db;
@@ -140,9 +137,7 @@ class CheckSetupControllerTest extends TestCase {
->willReturnCallback(function ($message, array $replace) {
return vsprintf($message, $replace);
});
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->dispatcher = $this->getMockBuilder(EventDispatcherInterface::class)
- ->disableOriginalConstructor()->getMock();
+ $this->dispatcher = $this->createMock(IEventDispatcher::class);
$this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
->disableOriginalConstructor()->getMock();
$this->logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
@@ -171,7 +166,6 @@ class CheckSetupControllerTest extends TestCase {
$this->l10n,
$this->checker,
$this->logger,
- $this->eventDispatcher,
$this->dispatcher,
$this->db,
$this->lockingProvider,
@@ -681,7 +675,6 @@ class CheckSetupControllerTest extends TestCase {
$this->l10n,
$this->checker,
$this->logger,
- $this->eventDispatcher,
$this->dispatcher,
$this->db,
$this->lockingProvider,
@@ -1409,7 +1402,6 @@ Array
$this->l10n,
$this->checker,
$this->logger,
- $this->eventDispatcher,
$this->dispatcher,
$this->db,
$this->lockingProvider,
@@ -1464,7 +1456,6 @@ Array
$this->l10n,
$this->checker,
$this->logger,
- $this->eventDispatcher,
$this->dispatcher,
$this->db,
$this->lockingProvider,
diff --git a/apps/theming/css/default.css b/apps/theming/css/default.css
index 71b4cca6ff3..66c355f1492 100644
--- a/apps/theming/css/default.css
+++ b/apps/theming/css/default.css
@@ -72,7 +72,7 @@
--color-primary-light-text: #002a41;
--color-primary-light-hover: #dbe5ea;
--color-primary-element: #006aa3;
- --color-primary-element-hover: #3287b5;
+ --color-primary-element-hover: #1f7cae;
--color-primary-element-text: #ffffff;
--color-primary-element-light: #e5f0f5;
--color-primary-element-light-hover: #dbe5ea;
diff --git a/apps/theming/lib/Themes/CommonThemeTrait.php b/apps/theming/lib/Themes/CommonThemeTrait.php
index 3a14382d3c0..1aa1174fabc 100644
--- a/apps/theming/lib/Themes/CommonThemeTrait.php
+++ b/apps/theming/lib/Themes/CommonThemeTrait.php
@@ -64,7 +64,7 @@ trait CommonThemeTrait {
// used for buttons, inputs...
'--color-primary-element' => $colorPrimaryElement,
- '--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 60),
+ '--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 75),
'--color-primary-element-text' => $this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff',
// used for hover/focus states
diff --git a/apps/twofactor_backupcodes/l10n/fa.js b/apps/twofactor_backupcodes/l10n/fa.js
new file mode 100644
index 00000000000..b5f081d359b
--- /dev/null
+++ b/apps/twofactor_backupcodes/l10n/fa.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "twofactor_backupcodes",
+ {
+ "You created two-factor backup codes for your account" : "شما کدهای پشتیبان دو مرحله ای برای حساب خود ایجاد کردید.",
+ "Second-factor backup codes" : "کدهای پشتیبان فاکتور دوم",
+ "Generate backup codes" : "کدهای پشتیبان تولید کنید",
+ "You enabled two-factor authentication but did not generate backup codes yet. They are needed to restore access to your account in case you lose your second factor." : "شما احراز هویت دو مرحله ای را فعال کرده اید اما هنوز کدهای پشتیبان ایجاد نکرده اید. آنها برای بازگرداندن دسترسی به حساب شما در صورت از دست دادن فاکتور دوم مورد نیاز هستند.",
+ "Backup code" : "کد پشتیبان",
+ "Use backup code" : "از کد پشتیبان استفاده شود",
+ "Two factor backup codes" : "کدهای پشتیبان دو عاملی",
+ "A two-factor auth backup codes provider" : "ارائه دهنده کدهای پشتیبان تأیید اعتبار دو مرحله ای",
+ "Backup codes have been generated. {used} of {total} codes have been used." : "کدهای پشتیبان ایجاد شده است. {used} از {total} کد استفاده شده است.",
+ "These are your backup codes. Please save and/or print them as you will not be able to read the codes again later" : "اینها کدهای پشتیبان شما هستند. لطفاً آنها را ذخیره و/یا چاپ کنید زیرا بعداً نمی توانید دوباره کدها را بخوانید.",
+ "Save backup codes" : "ذخیره کدهای پشتیبان",
+ "Print backup codes" : "چاپ کدهای پشتیبان",
+ "Regenerate backup codes" : "کدهای پشتیبان را دوباره تولید کنید.",
+ "If you regenerate backup codes, you automatically invalidate old codes." : "اگر کدهای پشتیبان را دوباره تولید کنید، به طور خودکار کدهای قدیمی را باطل می کنید.",
+ "An error occurred while generating your backup codes" : "هنگام ایجاد کدهای پشتیبان شما خطایی روی داد",
+ "{name} backup codes" : "کدهای پشتیبان {نام}",
+ "Use one of the backup codes you saved when setting up two-factor authentication." : "از یکی از کدهای پشتیبان که هنگام تنظیم احراز هویت دو مرحله ای ذخیره کرده اید استفاده کنید.",
+ "Submit" : "ارسال"
+},
+"nplurals=2; plural=(n > 1);");
diff --git a/apps/twofactor_backupcodes/l10n/fa.json b/apps/twofactor_backupcodes/l10n/fa.json
new file mode 100644
index 00000000000..32c0691e395
--- /dev/null
+++ b/apps/twofactor_backupcodes/l10n/fa.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "You created two-factor backup codes for your account" : "شما کدهای پشتیبان دو مرحله ای برای حساب خود ایجاد کردید.",
+ "Second-factor backup codes" : "کدهای پشتیبان فاکتور دوم",
+ "Generate backup codes" : "کدهای پشتیبان تولید کنید",
+ "You enabled two-factor authentication but did not generate backup codes yet. They are needed to restore access to your account in case you lose your second factor." : "شما احراز هویت دو مرحله ای را فعال کرده اید اما هنوز کدهای پشتیبان ایجاد نکرده اید. آنها برای بازگرداندن دسترسی به حساب شما در صورت از دست دادن فاکتور دوم مورد نیاز هستند.",
+ "Backup code" : "کد پشتیبان",
+ "Use backup code" : "از کد پشتیبان استفاده شود",
+ "Two factor backup codes" : "کدهای پشتیبان دو عاملی",
+ "A two-factor auth backup codes provider" : "ارائه دهنده کدهای پشتیبان تأیید اعتبار دو مرحله ای",
+ "Backup codes have been generated. {used} of {total} codes have been used." : "کدهای پشتیبان ایجاد شده است. {used} از {total} کد استفاده شده است.",
+ "These are your backup codes. Please save and/or print them as you will not be able to read the codes again later" : "اینها کدهای پشتیبان شما هستند. لطفاً آنها را ذخیره و/یا چاپ کنید زیرا بعداً نمی توانید دوباره کدها را بخوانید.",
+ "Save backup codes" : "ذخیره کدهای پشتیبان",
+ "Print backup codes" : "چاپ کدهای پشتیبان",
+ "Regenerate backup codes" : "کدهای پشتیبان را دوباره تولید کنید.",
+ "If you regenerate backup codes, you automatically invalidate old codes." : "اگر کدهای پشتیبان را دوباره تولید کنید، به طور خودکار کدهای قدیمی را باطل می کنید.",
+ "An error occurred while generating your backup codes" : "هنگام ایجاد کدهای پشتیبان شما خطایی روی داد",
+ "{name} backup codes" : "کدهای پشتیبان {نام}",
+ "Use one of the backup codes you saved when setting up two-factor authentication." : "از یکی از کدهای پشتیبان که هنگام تنظیم احراز هویت دو مرحله ای ذخیره کرده اید استفاده کنید.",
+ "Submit" : "ارسال"
+},"pluralForm" :"nplurals=2; plural=(n > 1);"
+} \ No newline at end of file
diff --git a/apps/workflowengine/l10n/fr.js b/apps/workflowengine/l10n/fr.js
index caeac79b650..dc09ec7be42 100644
--- a/apps/workflowengine/l10n/fr.js
+++ b/apps/workflowengine/l10n/fr.js
@@ -67,11 +67,12 @@ OC.L10N.register(
"Desktop client" : "Client de bureau",
"Thunderbird & Outlook addons" : "Modules complémentaires Thunderbird & Outlook",
"Custom user agent" : "Agent utilisateur personnalisé",
+ "Select a trigger" : "Sélectionner un déclencheur",
"At least one event must be selected" : "Au moins un événement doit être sélectionné",
"Add new flow" : "Ajouter un nouveau flux",
"When" : "Quand",
"and" : "et",
- "Add a new filter" : "Ajouter un filtre",
+ "Add a new filter" : "Ajouter une condition",
"Cancel" : "Annuler",
"Delete" : "Supprimer",
"The configuration is invalid" : "Configuration non valide",
diff --git a/apps/workflowengine/l10n/fr.json b/apps/workflowengine/l10n/fr.json
index 69e1488b8b2..ed7e4204ced 100644
--- a/apps/workflowengine/l10n/fr.json
+++ b/apps/workflowengine/l10n/fr.json
@@ -65,11 +65,12 @@
"Desktop client" : "Client de bureau",
"Thunderbird & Outlook addons" : "Modules complémentaires Thunderbird & Outlook",
"Custom user agent" : "Agent utilisateur personnalisé",
+ "Select a trigger" : "Sélectionner un déclencheur",
"At least one event must be selected" : "Au moins un événement doit être sélectionné",
"Add new flow" : "Ajouter un nouveau flux",
"When" : "Quand",
"and" : "et",
- "Add a new filter" : "Ajouter un filtre",
+ "Add a new filter" : "Ajouter une condition",
"Cancel" : "Annuler",
"Delete" : "Supprimer",
"The configuration is invalid" : "Configuration non valide",
diff --git a/core/Application.php b/core/Application.php
index 592e0929666..307d839f07c 100644
--- a/core/Application.php
+++ b/core/Application.php
@@ -32,7 +32,6 @@
*/
namespace OC\Core;
-use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
use OC\Authentication\Events\RemoteWipeFinished;
use OC\Authentication\Events\RemoteWipeStarted;
use OC\Authentication\Listeners\RemoteWipeActivityListener;
@@ -45,25 +44,22 @@ use OC\Authentication\Listeners\UserDeletedWebAuthnCleanupListener;
use OC\Authentication\Notifications\Notifier as AuthenticationNotifier;
use OC\Core\Listener\BeforeTemplateRenderedListener;
use OC\Core\Notification\CoreNotifier;
-use OC\DB\Connection;
-use OC\DB\MissingColumnInformation;
-use OC\DB\MissingIndexInformation;
-use OC\DB\MissingPrimaryKeyInformation;
-use OC\DB\SchemaWrapper;
use OC\Metadata\FileEventListener;
use OC\TagManager;
use OCP\AppFramework\App;
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
+use OCP\DB\Events\AddMissingColumnsEvent;
+use OCP\DB\Events\AddMissingIndicesEvent;
+use OCP\DB\Events\AddMissingPrimaryKeyEvent;
+use OCP\DB\Types;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Events\Node\NodeDeletedEvent;
use OCP\Files\Events\Node\NodeWrittenEvent;
use OCP\Files\Events\NodeRemovedFromCache;
-use OCP\IDBConnection;
use OCP\User\Events\BeforeUserDeletedEvent;
use OCP\User\Events\UserDeletedEvent;
use OCP\Util;
use OCP\IConfig;
-use Symfony\Component\EventDispatcher\GenericEvent;
/**
* Class Application
@@ -88,244 +84,237 @@ class Application extends App {
$notificationManager->registerNotifierService(CoreNotifier::class);
$notificationManager->registerNotifierService(AuthenticationNotifier::class);
- $oldEventDispatcher = $server->getEventDispatcher();
-
- $oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_INDEXES_EVENT,
- function (GenericEvent $event) use ($container) {
- /** @var MissingIndexInformation $subject */
- $subject = $event->getSubject();
-
- $schema = new SchemaWrapper($container->query(Connection::class));
-
- if ($schema->hasTable('share')) {
- $table = $schema->getTable('share');
-
- if (!$table->hasIndex('share_with_index')) {
- $subject->addHintForMissingSubject($table->getName(), 'share_with_index');
- }
- if (!$table->hasIndex('parent_index')) {
- $subject->addHintForMissingSubject($table->getName(), 'parent_index');
- }
- if (!$table->hasIndex('owner_index')) {
- $subject->addHintForMissingSubject($table->getName(), 'owner_index');
- }
- if (!$table->hasIndex('initiator_index')) {
- $subject->addHintForMissingSubject($table->getName(), 'initiator_index');
- }
- }
-
- if ($schema->hasTable('filecache')) {
- $table = $schema->getTable('filecache');
-
- if (!$table->hasIndex('fs_mtime')) {
- $subject->addHintForMissingSubject($table->getName(), 'fs_mtime');
- }
-
- if (!$table->hasIndex('fs_size')) {
- $subject->addHintForMissingSubject($table->getName(), 'fs_size');
- }
-
- if (!$table->hasIndex('fs_id_storage_size')) {
- $subject->addHintForMissingSubject($table->getName(), 'fs_id_storage_size');
- }
-
- if (!$table->hasIndex('fs_storage_path_prefix') && !$schema->getDatabasePlatform() instanceof PostgreSQL94Platform) {
- $subject->addHintForMissingSubject($table->getName(), 'fs_storage_path_prefix');
- }
-
- if (!$table->hasIndex('fs_parent')) {
- $subject->addHintForMissingSubject($table->getName(), 'fs_parent');
- }
- }
-
- if ($schema->hasTable('twofactor_providers')) {
- $table = $schema->getTable('twofactor_providers');
-
- if (!$table->hasIndex('twofactor_providers_uid')) {
- $subject->addHintForMissingSubject($table->getName(), 'twofactor_providers_uid');
- }
- }
-
- if ($schema->hasTable('login_flow_v2')) {
- $table = $schema->getTable('login_flow_v2');
-
- if (!$table->hasIndex('poll_token')) {
- $subject->addHintForMissingSubject($table->getName(), 'poll_token');
- }
- if (!$table->hasIndex('login_token')) {
- $subject->addHintForMissingSubject($table->getName(), 'login_token');
- }
- if (!$table->hasIndex('timestamp')) {
- $subject->addHintForMissingSubject($table->getName(), 'timestamp');
- }
- }
-
- if ($schema->hasTable('whats_new')) {
- $table = $schema->getTable('whats_new');
-
- if (!$table->hasIndex('version')) {
- $subject->addHintForMissingSubject($table->getName(), 'version');
- }
- }
-
- if ($schema->hasTable('cards')) {
- $table = $schema->getTable('cards');
-
- if (!$table->hasIndex('cards_abid')) {
- $subject->addHintForMissingSubject($table->getName(), 'cards_abid');
- }
-
- if (!$table->hasIndex('cards_abiduri')) {
- $subject->addHintForMissingSubject($table->getName(), 'cards_abiduri');
- }
- }
-
- if ($schema->hasTable('cards_properties')) {
- $table = $schema->getTable('cards_properties');
-
- if (!$table->hasIndex('cards_prop_abid')) {
- $subject->addHintForMissingSubject($table->getName(), 'cards_prop_abid');
- }
- }
-
- if ($schema->hasTable('calendarobjects_props')) {
- $table = $schema->getTable('calendarobjects_props');
-
- if (!$table->hasIndex('calendarobject_calid_index')) {
- $subject->addHintForMissingSubject($table->getName(), 'calendarobject_calid_index');
- }
- }
-
- if ($schema->hasTable('schedulingobjects')) {
- $table = $schema->getTable('schedulingobjects');
- if (!$table->hasIndex('schedulobj_principuri_index')) {
- $subject->addHintForMissingSubject($table->getName(), 'schedulobj_principuri_index');
- }
- }
-
- if ($schema->hasTable('properties')) {
- $table = $schema->getTable('properties');
- if (!$table->hasIndex('properties_path_index')) {
- $subject->addHintForMissingSubject($table->getName(), 'properties_path_index');
- }
- if (!$table->hasIndex('properties_pathonly_index')) {
- $subject->addHintForMissingSubject($table->getName(), 'properties_pathonly_index');
- }
- }
-
- if ($schema->hasTable('jobs')) {
- $table = $schema->getTable('jobs');
- if (!$table->hasIndex('job_lastcheck_reserved')) {
- $subject->addHintForMissingSubject($table->getName(), 'job_lastcheck_reserved');
- }
- }
-
- if ($schema->hasTable('direct_edit')) {
- $table = $schema->getTable('direct_edit');
- if (!$table->hasIndex('direct_edit_timestamp')) {
- $subject->addHintForMissingSubject($table->getName(), 'direct_edit_timestamp');
- }
- }
-
- if ($schema->hasTable('preferences')) {
- $table = $schema->getTable('preferences');
- if (!$table->hasIndex('preferences_app_key')) {
- $subject->addHintForMissingSubject($table->getName(), 'preferences_app_key');
- }
- }
-
- if ($schema->hasTable('mounts')) {
- $table = $schema->getTable('mounts');
- if (!$table->hasIndex('mounts_class_index')) {
- $subject->addHintForMissingSubject($table->getName(), 'mounts_class_index');
- }
- if (!$table->hasIndex('mounts_user_root_path_index')) {
- $subject->addHintForMissingSubject($table->getName(), 'mounts_user_root_path_index');
- }
- }
-
- if ($schema->hasTable('systemtag_object_mapping')) {
- $table = $schema->getTable('systemtag_object_mapping');
- if (!$table->hasIndex('systag_by_tagid')) {
- $subject->addHintForMissingSubject($table->getName(), 'systag_by_tagid');
- }
- }
- }
- );
-
- $oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_PRIMARY_KEYS_EVENT,
- function (GenericEvent $event) use ($container) {
- /** @var MissingPrimaryKeyInformation $subject */
- $subject = $event->getSubject();
-
- $schema = new SchemaWrapper($container->query(Connection::class));
-
- if ($schema->hasTable('federated_reshares')) {
- $table = $schema->getTable('federated_reshares');
-
- if (!$table->hasPrimaryKey()) {
- $subject->addHintForMissingSubject($table->getName());
- }
- }
-
- if ($schema->hasTable('systemtag_object_mapping')) {
- $table = $schema->getTable('systemtag_object_mapping');
-
- if (!$table->hasPrimaryKey()) {
- $subject->addHintForMissingSubject($table->getName());
- }
- }
-
- if ($schema->hasTable('comments_read_markers')) {
- $table = $schema->getTable('comments_read_markers');
-
- if (!$table->hasPrimaryKey()) {
- $subject->addHintForMissingSubject($table->getName());
- }
- }
-
- if ($schema->hasTable('collres_resources')) {
- $table = $schema->getTable('collres_resources');
-
- if (!$table->hasPrimaryKey()) {
- $subject->addHintForMissingSubject($table->getName());
- }
- }
-
- if ($schema->hasTable('collres_accesscache')) {
- $table = $schema->getTable('collres_accesscache');
-
- if (!$table->hasPrimaryKey()) {
- $subject->addHintForMissingSubject($table->getName());
- }
- }
-
- if ($schema->hasTable('filecache_extended')) {
- $table = $schema->getTable('filecache_extended');
-
- if (!$table->hasPrimaryKey()) {
- $subject->addHintForMissingSubject($table->getName());
- }
- }
- }
- );
-
- $oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_COLUMNS_EVENT,
- function (GenericEvent $event) use ($container) {
- /** @var MissingColumnInformation $subject */
- $subject = $event->getSubject();
-
- $schema = new SchemaWrapper($container->query(Connection::class));
-
- if ($schema->hasTable('comments')) {
- $table = $schema->getTable('comments');
-
- if (!$table->hasColumn('reference_id')) {
- $subject->addHintForMissingColumn($table->getName(), 'reference_id');
- }
- }
- }
- );
+ $eventDispatcher->addListener(AddMissingIndicesEvent::class, function (AddMissingIndicesEvent $event) {
+ $event->addMissingIndex(
+ 'share',
+ 'share_with_index',
+ ['share_with']
+ );
+ $event->addMissingIndex(
+ 'share',
+ 'parent_index',
+ ['parent']
+ );
+ $event->addMissingIndex(
+ 'share',
+ 'owner_index',
+ ['uid_owner']
+ );
+ $event->addMissingIndex(
+ 'share',
+ 'initiator_index',
+ ['uid_initiator']
+ );
+
+ $event->addMissingIndex(
+ 'filecache',
+ 'fs_mtime',
+ ['mtime']
+ );
+ $event->addMissingIndex(
+ 'filecache',
+ 'fs_size',
+ ['size']
+ );
+ $event->addMissingIndex(
+ 'filecache',
+ 'fs_id_storage_size',
+ ['fileid', 'storage', 'size']
+ );
+ $event->addMissingIndex(
+ 'filecache',
+ 'fs_storage_path_prefix',
+ ['storage', 'path'],
+ ['lengths' => [null, 64]]
+ );
+ $event->addMissingIndex(
+ 'filecache',
+ 'fs_parent',
+ ['parent']
+ );
+
+ $event->addMissingIndex(
+ 'twofactor_providers',
+ 'twofactor_providers_uid',
+ ['uid']
+ );
+
+ $event->addMissingUniqueIndex(
+ 'login_flow_v2',
+ 'poll_token',
+ ['poll_token'],
+ [],
+ true
+ );
+ $event->addMissingUniqueIndex(
+ 'login_flow_v2',
+ 'login_token',
+ ['login_token'],
+ [],
+ true
+ );
+ $event->addMissingIndex(
+ 'login_flow_v2',
+ 'timestamp',
+ ['timestamp'],
+ [],
+ true
+ );
+
+ $event->addMissingIndex(
+ 'whats_new',
+ 'version',
+ ['version'],
+ [],
+ true
+ );
+
+ $event->addMissingIndex(
+ 'cards',
+ 'cards_abiduri',
+ ['addressbookid', 'uri'],
+ [],
+ true
+ );
+ $event->addMissingIndex(
+ 'cards',
+ 'cards_abid',
+ ['addressbookid'],
+ [],
+ true
+ );
+ $event->addMissingIndex(
+ 'cards',
+ 'cards_abiduri',
+ ['addressbookid', 'uri'],
+ [],
+ true
+ );
+
+ $event->addMissingIndex(
+ 'cards_properties',
+ 'cards_prop_abid',
+ ['addressbookid'],
+ [],
+ true
+ );
+
+ $event->addMissingIndex(
+ 'calendarobjects_props',
+ 'calendarobject_calid_index',
+ ['calendarid', 'calendartype']
+ );
+
+ $event->addMissingIndex(
+ 'schedulingobjects',
+ 'schedulobj_principuri_index',
+ ['principaluri']
+ );
+
+ $event->addMissingIndex(
+ 'properties',
+ 'properties_path_index',
+ ['userid', 'propertypath']
+ );
+ $event->addMissingIndex(
+ 'properties',
+ 'properties_pathonly_index',
+ ['propertypath']
+ );
+
+
+ $event->addMissingIndex(
+ 'jobs',
+ 'job_lastcheck_reserved',
+ ['last_checked', 'reserved_at']
+ );
+
+ $event->addMissingIndex(
+ 'direct_edit',
+ 'direct_edit_timestamp',
+ ['timestamp']
+ );
+
+ $event->addMissingIndex(
+ 'preferences',
+ 'preferences_app_key',
+ ['appid', 'configkey']
+ );
+
+ $event->addMissingIndex(
+ 'mounts',
+ 'mounts_class_index',
+ ['mount_provider_class']
+ );
+ $event->addMissingIndex(
+ 'mounts',
+ 'mounts_user_root_path_index',
+ ['user_id', 'root_id', 'mount_point'],
+ ['lengths' => [null, null, 128]]
+ );
+
+ $event->addMissingIndex(
+ 'systemtag_object_mapping',
+ 'systag_by_tagid',
+ ['systemtagid', 'objecttype']
+ );
+ });
+
+ $eventDispatcher->addListener(AddMissingPrimaryKeyEvent::class, function (AddMissingPrimaryKeyEvent $event) {
+ $event->addMissingPrimaryKey(
+ 'federated_reshares',
+ 'federated_res_pk',
+ ['share_id'],
+ 'share_id_index'
+ );
+
+ $event->addMissingPrimaryKey(
+ 'systemtag_object_mapping',
+ 'som_pk',
+ ['objecttype', 'objectid', 'systemtagid'],
+ 'mapping'
+ );
+
+ $event->addMissingPrimaryKey(
+ 'comments_read_markers',
+ 'crm_pk',
+ ['user_id', 'object_type', 'object_id'],
+ 'comments_marker_index'
+ );
+
+ $event->addMissingPrimaryKey(
+ 'collres_resources',
+ 'crr_pk',
+ ['collection_id', 'resource_type', 'resource_id'],
+ 'collres_unique_res'
+ );
+
+ $event->addMissingPrimaryKey(
+ 'collres_accesscache',
+ 'cra_pk',
+ ['user_id', 'collection_id', 'resource_type', 'resource_id'],
+ 'collres_unique_user'
+ );
+
+ $event->addMissingPrimaryKey(
+ 'filecache_extended',
+ 'fce_pk',
+ ['fileid'],
+ 'fce_fileid_idx'
+ );
+ });
+
+ $eventDispatcher->addListener(AddMissingColumnsEvent::class, function (AddMissingColumnsEvent $event) {
+ $event->addMissingColumn(
+ 'comments',
+ 'reference_id',
+ Types::STRING,
+ [
+ 'notnull' => false,
+ 'length' => 64,
+ ]
+ );
+ });
$eventDispatcher->addServiceListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
$eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeActivityListener::class);
diff --git a/core/Command/Db/AddMissingColumns.php b/core/Command/Db/AddMissingColumns.php
index 8e6f439e0c4..07763c66154 100644
--- a/core/Command/Db/AddMissingColumns.php
+++ b/core/Command/Db/AddMissingColumns.php
@@ -28,13 +28,12 @@ namespace OC\Core\Command\Db;
use OC\DB\Connection;
use OC\DB\SchemaWrapper;
-use OCP\IDBConnection;
+use OCP\DB\Events\AddMissingColumnsEvent;
+use OCP\EventDispatcher\IEventDispatcher;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\GenericEvent;
/**
* Class AddMissingColumns
@@ -47,7 +46,7 @@ use Symfony\Component\EventDispatcher\GenericEvent;
class AddMissingColumns extends Command {
public function __construct(
private Connection $connection,
- private EventDispatcherInterface $dispatcher,
+ private IEventDispatcher $dispatcher,
) {
parent::__construct();
}
@@ -60,46 +59,38 @@ class AddMissingColumns extends Command {
}
protected function execute(InputInterface $input, OutputInterface $output): int {
- $this->addCoreColumns($output, $input->getOption('dry-run'));
+ $dryRun = $input->getOption('dry-run');
// Dispatch event so apps can also update columns if needed
- $event = new GenericEvent($output);
- $this->dispatcher->dispatch(IDBConnection::ADD_MISSING_COLUMNS_EVENT, $event);
- return 0;
- }
-
- /**
- * add missing indices to the share table
- *
- * @param OutputInterface $output
- * @param bool $dryRun If true, will return the sql queries instead of running them.
- * @throws \Doctrine\DBAL\Schema\SchemaException
- */
- private function addCoreColumns(OutputInterface $output, bool $dryRun): void {
- $output->writeln('<info>Check columns of the comments table.</info>');
-
- $schema = new SchemaWrapper($this->connection);
+ $event = new AddMissingColumnsEvent();
+ $this->dispatcher->dispatchTyped($event);
+ $missingColumns = $event->getMissingColumns();
$updated = false;
- if ($schema->hasTable('comments')) {
- $table = $schema->getTable('comments');
- if (!$table->hasColumn('reference_id')) {
- $output->writeln('<info>Adding additional reference_id column to the comments table, this can take some time...</info>');
- $table->addColumn('reference_id', 'string', [
- 'notnull' => false,
- 'length' => 64,
- ]);
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
+ if (!empty($missingColumns)) {
+ $schema = new SchemaWrapper($this->connection);
+
+ foreach ($missingColumns as $missingColumn) {
+ if ($schema->hasTable($missingColumn['tableName'])) {
+ $table = $schema->getTable($missingColumn['tableName']);
+ if (!$table->hasColumn($missingColumn['columnName'])) {
+ $output->writeln('<info>Adding additional ' . $missingColumn['columnName'] . ' column to the ' . $missingColumn['tableName'] . ' table, this can take some time...</info>');
+ $table->addColumn($missingColumn['columnName'], $missingColumn['typeName'], $missingColumn['options']);
+ $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
+ if ($dryRun && $sqlQueries !== null) {
+ $output->writeln($sqlQueries);
+ }
+ $updated = true;
+ $output->writeln('<info>' . $missingColumn['tableName'] . ' table updated successfully.</info>');
+ }
}
- $updated = true;
- $output->writeln('<info>Comments table updated successfully.</info>');
}
}
if (!$updated) {
$output->writeln('<info>Done.</info>');
}
+
+ return 0;
}
}
diff --git a/core/Command/Db/AddMissingIndices.php b/core/Command/Db/AddMissingIndices.php
index dee9796a569..56dbf8ce8d9 100644
--- a/core/Command/Db/AddMissingIndices.php
+++ b/core/Command/Db/AddMissingIndices.php
@@ -33,18 +33,14 @@ declare(strict_types=1);
*/
namespace OC\Core\Command\Db;
-use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
use OC\DB\Connection;
use OC\DB\SchemaWrapper;
use OCP\DB\Events\AddMissingIndicesEvent;
use OCP\EventDispatcher\IEventDispatcher;
-use OCP\IDBConnection;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\GenericEvent;
/**
* Class AddMissingIndices
@@ -57,8 +53,7 @@ use Symfony\Component\EventDispatcher\GenericEvent;
class AddMissingIndices extends Command {
public function __construct(
private Connection $connection,
- private IEventDispatcher $eventDispatcher,
- private EventDispatcherInterface $dispatcher,
+ private IEventDispatcher $dispatcher,
) {
parent::__construct();
}
@@ -73,14 +68,9 @@ class AddMissingIndices extends Command {
protected function execute(InputInterface $input, OutputInterface $output): int {
$dryRun = $input->getOption('dry-run');
- $this->addCoreIndexes($output, $dryRun);
-
// Dispatch event so apps can also update indexes if needed
- $event = new GenericEvent($output);
- $this->dispatcher->dispatch(IDBConnection::ADD_MISSING_INDEXES_EVENT, $event);
-
$event = new AddMissingIndicesEvent();
- $this->eventDispatcher->dispatchTyped($event);
+ $this->dispatcher->dispatchTyped($event);
$missingIndices = $event->getMissingIndices();
if ($missingIndices !== []) {
@@ -91,7 +81,23 @@ class AddMissingIndices extends Command {
$table = $schema->getTable($missingIndex['tableName']);
if (!$table->hasIndex($missingIndex['indexName'])) {
$output->writeln('<info>Adding additional ' . $missingIndex['indexName'] . ' index to the ' . $table->getName() . ' table, this can take some time...</info>');
- $table->addIndex($missingIndex['columns'], $missingIndex['indexName']);
+
+ if ($missingIndex['dropUnnamedIndex']) {
+ foreach ($table->getIndexes() as $index) {
+ $columns = $index->getColumns();
+ if ($columns === $missingIndex['columns']) {
+ $table->dropIndex($index->getName());
+ }
+ }
+ }
+
+ if ($missingIndex['uniqueIndex']) {
+ $table->addUniqueIndex($missingIndex['columns'], $missingIndex['indexName'], $missingIndex['options']);
+ } else {
+ $table->addIndex($missingIndex['columns'], $missingIndex['indexName'], [], $missingIndex['options']);
+ }
+
+
$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
if ($dryRun && $sqlQueries !== null) {
$output->writeln($sqlQueries);
@@ -104,418 +110,4 @@ class AddMissingIndices extends Command {
return 0;
}
-
- /**
- * add missing indices to the share table
- *
- * @param OutputInterface $output
- * @param bool $dryRun If true, will return the sql queries instead of running them.
- * @throws \Doctrine\DBAL\Schema\SchemaException
- */
- private function addCoreIndexes(OutputInterface $output, bool $dryRun): void {
- $output->writeln('<info>Check indices of the share table.</info>');
-
- $schema = new SchemaWrapper($this->connection);
- $updated = false;
-
- if ($schema->hasTable('share')) {
- $table = $schema->getTable('share');
- if (!$table->hasIndex('share_with_index')) {
- $output->writeln('<info>Adding additional share_with index to the share table, this can take some time...</info>');
- $table->addIndex(['share_with'], 'share_with_index');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>Share table updated successfully.</info>');
- }
-
- if (!$table->hasIndex('parent_index')) {
- $output->writeln('<info>Adding additional parent index to the share table, this can take some time...</info>');
- $table->addIndex(['parent'], 'parent_index');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>Share table updated successfully.</info>');
- }
-
- if (!$table->hasIndex('owner_index')) {
- $output->writeln('<info>Adding additional owner index to the share table, this can take some time...</info>');
- $table->addIndex(['uid_owner'], 'owner_index');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>Share table updated successfully.</info>');
- }
-
- if (!$table->hasIndex('initiator_index')) {
- $output->writeln('<info>Adding additional initiator index to the share table, this can take some time...</info>');
- $table->addIndex(['uid_initiator'], 'initiator_index');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>Share table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the filecache table.</info>');
- if ($schema->hasTable('filecache')) {
- $table = $schema->getTable('filecache');
- if (!$table->hasIndex('fs_mtime')) {
- $output->writeln('<info>Adding additional mtime index to the filecache table, this can take some time...</info>');
- $table->addIndex(['mtime'], 'fs_mtime');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>Filecache table updated successfully.</info>');
- }
- if (!$table->hasIndex('fs_size')) {
- $output->writeln('<info>Adding additional size index to the filecache table, this can take some time...</info>');
- $table->addIndex(['size'], 'fs_size');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>Filecache table updated successfully.</info>');
- }
- if (!$table->hasIndex('fs_id_storage_size')) {
- $output->writeln('<info>Adding additional size index to the filecache table, this can take some time...</info>');
- $table->addIndex(['fileid', 'storage', 'size'], 'fs_id_storage_size');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>Filecache table updated successfully.</info>');
- }
- if (!$table->hasIndex('fs_storage_path_prefix') && !$schema->getDatabasePlatform() instanceof PostgreSQL94Platform) {
- $output->writeln('<info>Adding additional path index to the filecache table, this can take some time...</info>');
- $table->addIndex(['storage', 'path'], 'fs_storage_path_prefix', [], ['lengths' => [null, 64]]);
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>Filecache table updated successfully.</info>');
- }
- if (!$table->hasIndex('fs_parent')) {
- $output->writeln('<info>Adding additional parent index to the filecache table, this can take some time...</info>');
- $table->addIndex(['parent'], 'fs_parent');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>Filecache table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the twofactor_providers table.</info>');
- if ($schema->hasTable('twofactor_providers')) {
- $table = $schema->getTable('twofactor_providers');
- if (!$table->hasIndex('twofactor_providers_uid')) {
- $output->writeln('<info>Adding additional twofactor_providers_uid index to the twofactor_providers table, this can take some time...</info>');
- $table->addIndex(['uid'], 'twofactor_providers_uid');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>Twofactor_providers table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the login_flow_v2 table.</info>');
- if ($schema->hasTable('login_flow_v2')) {
- $table = $schema->getTable('login_flow_v2');
- if (!$table->hasIndex('poll_token')) {
- $output->writeln('<info>Adding additional indeces to the login_flow_v2 table, this can take some time...</info>');
-
- foreach ($table->getIndexes() as $index) {
- $columns = $index->getColumns();
- if ($columns === ['poll_token'] ||
- $columns === ['login_token'] ||
- $columns === ['timestamp']) {
- $table->dropIndex($index->getName());
- }
- }
-
- $table->addUniqueIndex(['poll_token'], 'poll_token');
- $table->addUniqueIndex(['login_token'], 'login_token');
- $table->addIndex(['timestamp'], 'timestamp');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>login_flow_v2 table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the whats_new table.</info>');
- if ($schema->hasTable('whats_new')) {
- $table = $schema->getTable('whats_new');
- if (!$table->hasIndex('version')) {
- $output->writeln('<info>Adding version index to the whats_new table, this can take some time...</info>');
-
- foreach ($table->getIndexes() as $index) {
- if ($index->getColumns() === ['version']) {
- $table->dropIndex($index->getName());
- }
- }
-
- $table->addUniqueIndex(['version'], 'version');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>whats_new table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the cards table.</info>');
- $cardsUpdated = false;
- if ($schema->hasTable('cards')) {
- $table = $schema->getTable('cards');
-
- if ($table->hasIndex('addressbookid_uri_index')) {
- if ($table->hasIndex('cards_abiduri')) {
- $table->dropIndex('addressbookid_uri_index');
- } else {
- $output->writeln('<info>Renaming addressbookid_uri_index index to cards_abiduri in the cards table, this can take some time...</info>');
-
- foreach ($table->getIndexes() as $index) {
- if ($index->getColumns() === ['addressbookid', 'uri']) {
- $table->renameIndex('addressbookid_uri_index', 'cards_abiduri');
- }
- }
- }
-
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $cardsUpdated = true;
- }
-
- if (!$table->hasIndex('cards_abid')) {
- $output->writeln('<info>Adding cards_abid index to the cards table, this can take some time...</info>');
-
- foreach ($table->getIndexes() as $index) {
- if ($index->getColumns() === ['addressbookid']) {
- $table->dropIndex($index->getName());
- }
- }
-
- $table->addIndex(['addressbookid'], 'cards_abid');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $cardsUpdated = true;
- }
-
- if (!$table->hasIndex('cards_abiduri')) {
- $output->writeln('<info>Adding cards_abiduri index to the cards table, this can take some time...</info>');
-
- foreach ($table->getIndexes() as $index) {
- if ($index->getColumns() === ['addressbookid', 'uri']) {
- $table->dropIndex($index->getName());
- }
- }
-
- $table->addIndex(['addressbookid', 'uri'], 'cards_abiduri');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $cardsUpdated = true;
- }
-
- if ($cardsUpdated) {
- $updated = true;
- $output->writeln('<info>cards table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the cards_properties table.</info>');
- if ($schema->hasTable('cards_properties')) {
- $table = $schema->getTable('cards_properties');
- if (!$table->hasIndex('cards_prop_abid')) {
- $output->writeln('<info>Adding cards_prop_abid index to the cards_properties table, this can take some time...</info>');
-
- foreach ($table->getIndexes() as $index) {
- if ($index->getColumns() === ['addressbookid']) {
- $table->dropIndex($index->getName());
- }
- }
-
- $table->addIndex(['addressbookid'], 'cards_prop_abid');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>cards_properties table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the calendarobjects_props table.</info>');
- if ($schema->hasTable('calendarobjects_props')) {
- $table = $schema->getTable('calendarobjects_props');
- if (!$table->hasIndex('calendarobject_calid_index')) {
- $output->writeln('<info>Adding calendarobject_calid_index index to the calendarobjects_props table, this can take some time...</info>');
-
- $table->addIndex(['calendarid', 'calendartype'], 'calendarobject_calid_index');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>calendarobjects_props table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the schedulingobjects table.</info>');
- if ($schema->hasTable('schedulingobjects')) {
- $table = $schema->getTable('schedulingobjects');
- if (!$table->hasIndex('schedulobj_principuri_index')) {
- $output->writeln('<info>Adding schedulobj_principuri_index index to the schedulingobjects table, this can take some time...</info>');
-
- $table->addIndex(['principaluri'], 'schedulobj_principuri_index');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>schedulingobjects table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the oc_properties table.</info>');
- if ($schema->hasTable('properties')) {
- $table = $schema->getTable('properties');
- $propertiesUpdated = false;
-
- if (!$table->hasIndex('properties_path_index')) {
- $output->writeln('<info>Adding properties_path_index index to the oc_properties table, this can take some time...</info>');
-
- $table->addIndex(['userid', 'propertypath'], 'properties_path_index');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $propertiesUpdated = true;
- }
- if (!$table->hasIndex('properties_pathonly_index')) {
- $output->writeln('<info>Adding properties_pathonly_index index to the oc_properties table, this can take some time...</info>');
-
- $table->addIndex(['propertypath'], 'properties_pathonly_index');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $propertiesUpdated = true;
- }
-
- if ($propertiesUpdated) {
- $updated = true;
- $output->writeln('<info>oc_properties table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the oc_jobs table.</info>');
- if ($schema->hasTable('jobs')) {
- $table = $schema->getTable('jobs');
- if (!$table->hasIndex('job_lastcheck_reserved')) {
- $output->writeln('<info>Adding job_lastcheck_reserved index to the oc_jobs table, this can take some time...</info>');
-
- $table->addIndex(['last_checked', 'reserved_at'], 'job_lastcheck_reserved');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>oc_properties table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the oc_direct_edit table.</info>');
- if ($schema->hasTable('direct_edit')) {
- $table = $schema->getTable('direct_edit');
- if (!$table->hasIndex('direct_edit_timestamp')) {
- $output->writeln('<info>Adding direct_edit_timestamp index to the oc_direct_edit table, this can take some time...</info>');
-
- $table->addIndex(['timestamp'], 'direct_edit_timestamp');
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>oc_direct_edit table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the oc_preferences table.</info>');
- if ($schema->hasTable('preferences')) {
- $table = $schema->getTable('preferences');
- if (!$table->hasIndex('preferences_app_key')) {
- $output->writeln('<info>Adding preferences_app_key index to the oc_preferences table, this can take some time...</info>');
-
- $table->addIndex(['appid', 'configkey'], 'preferences_app_key');
- $this->connection->migrateToSchema($schema->getWrappedSchema());
- $updated = true;
- $output->writeln('<info>oc_properties table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the oc_mounts table.</info>');
- if ($schema->hasTable('mounts')) {
- $table = $schema->getTable('mounts');
- if (!$table->hasIndex('mounts_class_index')) {
- $output->writeln('<info>Adding mounts_class_index index to the oc_mounts table, this can take some time...</info>');
-
- $table->addIndex(['mount_provider_class'], 'mounts_class_index');
- $this->connection->migrateToSchema($schema->getWrappedSchema());
- $updated = true;
- $output->writeln('<info>oc_mounts table updated successfully.</info>');
- }
- if (!$table->hasIndex('mounts_user_root_path_index')) {
- $output->writeln('<info>Adding mounts_user_root_path_index index to the oc_mounts table, this can take some time...</info>');
-
- $table->addIndex(['user_id', 'root_id', 'mount_point'], 'mounts_user_root_path_index', [], ['lengths' => [null, null, 128]]);
- $this->connection->migrateToSchema($schema->getWrappedSchema());
- $updated = true;
- $output->writeln('<info>oc_mounts table updated successfully.</info>');
- }
- }
-
- $output->writeln('<info>Check indices of the oc_systemtag_object_mapping table.</info>');
- if ($schema->hasTable('systemtag_object_mapping')) {
- $table = $schema->getTable('systemtag_object_mapping');
- if (!$table->hasIndex('systag_by_tagid')) {
- $output->writeln('<info>Adding systag_by_tagid index to the oc_systemtag_object_mapping table, this can take some time...</info>');
-
- $table->addIndex(['systemtagid', 'objecttype'], 'systag_by_tagid');
- $this->connection->migrateToSchema($schema->getWrappedSchema());
- $updated = true;
- $output->writeln('<info>oc_systemtag_object_mapping table updated successfully.</info>');
- }
- }
-
- if (!$updated) {
- $output->writeln('<info>Done.</info>');
- }
- }
}
diff --git a/core/Command/Db/AddMissingPrimaryKeys.php b/core/Command/Db/AddMissingPrimaryKeys.php
index a11be78ccfc..658eb0b0f5a 100644
--- a/core/Command/Db/AddMissingPrimaryKeys.php
+++ b/core/Command/Db/AddMissingPrimaryKeys.php
@@ -28,13 +28,12 @@ namespace OC\Core\Command\Db;
use OC\DB\Connection;
use OC\DB\SchemaWrapper;
-use OCP\IDBConnection;
+use OCP\DB\Events\AddMissingPrimaryKeyEvent;
+use OCP\EventDispatcher\IEventDispatcher;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\GenericEvent;
/**
* Class AddMissingPrimaryKeys
@@ -47,7 +46,7 @@ use Symfony\Component\EventDispatcher\GenericEvent;
class AddMissingPrimaryKeys extends Command {
public function __construct(
private Connection $connection,
- private EventDispatcherInterface $dispatcher,
+ private IEventDispatcher $dispatcher,
) {
parent::__construct();
}
@@ -60,131 +59,44 @@ class AddMissingPrimaryKeys extends Command {
}
protected function execute(InputInterface $input, OutputInterface $output): int {
- $this->addCorePrimaryKeys($output, $input->getOption('dry-run'));
+ $dryRun = $input->getOption('dry-run');
// Dispatch event so apps can also update indexes if needed
- $event = new GenericEvent($output);
- $this->dispatcher->dispatch(IDBConnection::ADD_MISSING_PRIMARY_KEYS_EVENT, $event);
- return 0;
- }
-
- /**
- * add missing indices to the share table
- *
- * @param OutputInterface $output
- * @param bool $dryRun If true, will return the sql queries instead of running them.
- * @throws \Doctrine\DBAL\Schema\SchemaException
- */
- private function addCorePrimaryKeys(OutputInterface $output, bool $dryRun): void {
- $output->writeln('<info>Check primary keys.</info>');
-
- $schema = new SchemaWrapper($this->connection);
+ $event = new AddMissingPrimaryKeyEvent();
+ $this->dispatcher->dispatchTyped($event);
+ $missingKeys = $event->getMissingPrimaryKeys();
$updated = false;
- if ($schema->hasTable('federated_reshares')) {
- $table = $schema->getTable('federated_reshares');
- if (!$table->hasPrimaryKey()) {
- $output->writeln('<info>Adding primary key to the federated_reshares table, this can take some time...</info>');
- $table->setPrimaryKey(['share_id'], 'federated_res_pk');
- if ($table->hasIndex('share_id_index')) {
- $table->dropIndex('share_id_index');
- }
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>federated_reshares table updated successfully.</info>');
- }
- }
-
- if ($schema->hasTable('systemtag_object_mapping')) {
- $table = $schema->getTable('systemtag_object_mapping');
- if (!$table->hasPrimaryKey()) {
- $output->writeln('<info>Adding primary key to the systemtag_object_mapping table, this can take some time...</info>');
- $table->setPrimaryKey(['objecttype', 'objectid', 'systemtagid'], 'som_pk');
- if ($table->hasIndex('mapping')) {
- $table->dropIndex('mapping');
- }
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>systemtag_object_mapping table updated successfully.</info>');
- }
- }
+ if (!empty($missingKeys)) {
+ $schema = new SchemaWrapper($this->connection);
- if ($schema->hasTable('comments_read_markers')) {
- $table = $schema->getTable('comments_read_markers');
- if (!$table->hasPrimaryKey()) {
- $output->writeln('<info>Adding primary key to the comments_read_markers table, this can take some time...</info>');
- $table->setPrimaryKey(['user_id', 'object_type', 'object_id'], 'crm_pk');
- if ($table->hasIndex('comments_marker_index')) {
- $table->dropIndex('comments_marker_index');
- }
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>comments_read_markers table updated successfully.</info>');
- }
- }
+ foreach ($missingKeys as $missingKey) {
+ if ($schema->hasTable($missingKey['tableName'])) {
+ $table = $schema->getTable($missingKey['tableName']);
+ if (!$table->hasPrimaryKey()) {
+ $output->writeln('<info>Adding primary key to the ' . $missingKey['tableName'] . ' table, this can take some time...</info>');
+ $table->setPrimaryKey($missingKey['columns'], $missingKey['primaryKeyName']);
- if ($schema->hasTable('collres_resources')) {
- $table = $schema->getTable('collres_resources');
- if (!$table->hasPrimaryKey()) {
- $output->writeln('<info>Adding primary key to the collres_resources table, this can take some time...</info>');
- $table->setPrimaryKey(['collection_id', 'resource_type', 'resource_id'], 'crr_pk');
- if ($table->hasIndex('collres_unique_res')) {
- $table->dropIndex('collres_unique_res');
- }
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>collres_resources table updated successfully.</info>');
- }
- }
+ if ($missingKey['formerIndex'] && $table->hasIndex($missingKey['formerIndex'])) {
+ $table->dropIndex($missingKey['formerIndex']);
+ }
- if ($schema->hasTable('collres_accesscache')) {
- $table = $schema->getTable('collres_accesscache');
- if (!$table->hasPrimaryKey()) {
- $output->writeln('<info>Adding primary key to the collres_accesscache table, this can take some time...</info>');
- $table->setPrimaryKey(['user_id', 'collection_id', 'resource_type', 'resource_id'], 'cra_pk');
- if ($table->hasIndex('collres_unique_user')) {
- $table->dropIndex('collres_unique_user');
- }
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>collres_accesscache table updated successfully.</info>');
- }
- }
+ $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
+ if ($dryRun && $sqlQueries !== null) {
+ $output->writeln($sqlQueries);
+ }
- if ($schema->hasTable('filecache_extended')) {
- $table = $schema->getTable('filecache_extended');
- if (!$table->hasPrimaryKey()) {
- $output->writeln('<info>Adding primary key to the filecache_extended table, this can take some time...</info>');
- $table->setPrimaryKey(['fileid'], 'fce_pk');
- if ($table->hasIndex('fce_fileid_idx')) {
- $table->dropIndex('fce_fileid_idx');
+ $updated = true;
+ $output->writeln('<info>' . $missingKey['tableName'] . ' table updated successfully.</info>');
+ }
}
- $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
- if ($dryRun && $sqlQueries !== null) {
- $output->writeln($sqlQueries);
- }
- $updated = true;
- $output->writeln('<info>filecache_extended table updated successfully.</info>');
}
}
if (!$updated) {
$output->writeln('<info>Done.</info>');
}
+
+ return 0;
}
}
diff --git a/core/l10n/ar.js b/core/l10n/ar.js
index 6c754dc3eec..aac88a8958f 100644
--- a/core/l10n/ar.js
+++ b/core/l10n/ar.js
@@ -132,7 +132,7 @@ OC.L10N.register(
"The \"{header}\" HTTP header does not contain \"{expected}\". This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly." : "ترويسة إتش تي تي بي \"{header}\" لا تحتوي على \"{expected}\". و هذا يمثل خطراً أمنيّاً محتملاً. إذ من المفترض تعيين هذا الإعداد بحسب المُتّبع.",
"The \"{header}\" HTTP header is not set to \"{val1}\", \"{val2}\", \"{val3}\", \"{val4}\" or \"{val5}\". This can leak referer information. See the {linkstart}W3C Recommendation ↗{linkend}." : "لم يتم تعيين رأس HTTP \"{header}\" على \"{val1}\" أو \"{val2}\" أو \"{val3}\" أو \"{val4}\" أو \"{val5}\". يمكن أن يؤدي هذا إلى تسريب معلومات المرجع. راجع {linkstart} توصية W3C ↗{linkend}.",
"The \"Strict-Transport-Security\" HTTP header is not set to at least \"{seconds}\" seconds. For enhanced security, it is recommended to enable HSTS as described in the {linkstart}security tips ↗{linkend}." : "لم يتم تعيين رأس HTTP \"Strict-Transport-Security\" على \"{seconds}\" ثانية على الأقل. لتحسين الأمان ، يوصى بتمكين HSTS كما هو موضح في {linkstart} إرشادات الأمان ↗{linkend}.",
- "Accessing site insecurely via HTTP. You are strongly advised to set up your server to require HTTPS instead, as described in the {linkstart}security tips ↗{linkend}. Without it some important web functionality like \"copy to clipboard\" or \"service workers\" will not work!" : "الوصول إلى الموقع بشكل غير آمن عبر بروتوكول نقل النص المُتشعِّب. نوصي بشدة بإعداد الخادم الخاص بكم لفرض بروتوكول نقل النص الفائق الآمن بدلاً من ذلك، كما هو موضح في {linkstart} نصائح الأمان ↗ {linkend}. وبدونها، لن تعمل بعض وظائف الويب المهمة مثل \"نسخ إلى الحافظة\" أو \"أدوات الخدمة\"!",
+ "Accessing site insecurely via HTTP. You are strongly advised to set up your server to require HTTPS instead, as described in the {linkstart}security tips ↗{linkend}. Without it some important web functionality like \"copy to clipboard\" or \"service workers\" will not work!" : "الوصول إلى الموقع بشكل غير آمن عبر بروتوكول HTTP. نوصي بشدة بإعداد الخادم الخاص بكم لفرض بروتوكول HTTPS بدلاً من ذلك، كما هو موضح في {linkstart} نصائح الأمان ↗ {linkend}. وبدونها، لن تعمل بعض وظائف الويب المهمة مثل \"نسخ إلى الحافظة\" أو \"أدوات الخدمة\"!",
"unknown text" : "النص غير معروف",
"Hello world!" : "مرحبا بالعالم!",
"sunny" : "مشمس",
diff --git a/core/l10n/ar.json b/core/l10n/ar.json
index c1593651b4c..25c8f646f91 100644
--- a/core/l10n/ar.json
+++ b/core/l10n/ar.json
@@ -130,7 +130,7 @@
"The \"{header}\" HTTP header does not contain \"{expected}\". This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly." : "ترويسة إتش تي تي بي \"{header}\" لا تحتوي على \"{expected}\". و هذا يمثل خطراً أمنيّاً محتملاً. إذ من المفترض تعيين هذا الإعداد بحسب المُتّبع.",
"The \"{header}\" HTTP header is not set to \"{val1}\", \"{val2}\", \"{val3}\", \"{val4}\" or \"{val5}\". This can leak referer information. See the {linkstart}W3C Recommendation ↗{linkend}." : "لم يتم تعيين رأس HTTP \"{header}\" على \"{val1}\" أو \"{val2}\" أو \"{val3}\" أو \"{val4}\" أو \"{val5}\". يمكن أن يؤدي هذا إلى تسريب معلومات المرجع. راجع {linkstart} توصية W3C ↗{linkend}.",
"The \"Strict-Transport-Security\" HTTP header is not set to at least \"{seconds}\" seconds. For enhanced security, it is recommended to enable HSTS as described in the {linkstart}security tips ↗{linkend}." : "لم يتم تعيين رأس HTTP \"Strict-Transport-Security\" على \"{seconds}\" ثانية على الأقل. لتحسين الأمان ، يوصى بتمكين HSTS كما هو موضح في {linkstart} إرشادات الأمان ↗{linkend}.",
- "Accessing site insecurely via HTTP. You are strongly advised to set up your server to require HTTPS instead, as described in the {linkstart}security tips ↗{linkend}. Without it some important web functionality like \"copy to clipboard\" or \"service workers\" will not work!" : "الوصول إلى الموقع بشكل غير آمن عبر بروتوكول نقل النص المُتشعِّب. نوصي بشدة بإعداد الخادم الخاص بكم لفرض بروتوكول نقل النص الفائق الآمن بدلاً من ذلك، كما هو موضح في {linkstart} نصائح الأمان ↗ {linkend}. وبدونها، لن تعمل بعض وظائف الويب المهمة مثل \"نسخ إلى الحافظة\" أو \"أدوات الخدمة\"!",
+ "Accessing site insecurely via HTTP. You are strongly advised to set up your server to require HTTPS instead, as described in the {linkstart}security tips ↗{linkend}. Without it some important web functionality like \"copy to clipboard\" or \"service workers\" will not work!" : "الوصول إلى الموقع بشكل غير آمن عبر بروتوكول HTTP. نوصي بشدة بإعداد الخادم الخاص بكم لفرض بروتوكول HTTPS بدلاً من ذلك، كما هو موضح في {linkstart} نصائح الأمان ↗ {linkend}. وبدونها، لن تعمل بعض وظائف الويب المهمة مثل \"نسخ إلى الحافظة\" أو \"أدوات الخدمة\"!",
"unknown text" : "النص غير معروف",
"Hello world!" : "مرحبا بالعالم!",
"sunny" : "مشمس",
diff --git a/core/l10n/gl.js b/core/l10n/gl.js
index c167212a338..8ead0694f59 100644
--- a/core/l10n/gl.js
+++ b/core/l10n/gl.js
@@ -343,7 +343,7 @@ OC.L10N.register(
"Data folder" : "Cartafol de datos",
"Configure the database" : "Configurar a base de datos",
"Only %s is available." : "Só está dispoñíbel %s.",
- "Install and activate additional PHP modules to choose other database types." : "Instale e active os módulos de PHP adicionais para seleccionar outros tipos de bases de datos.",
+ "Install and activate additional PHP modules to choose other database types." : "Instale e active os módulos de PHP adicionais para escoller outros tipos de bases de datos.",
"For more details check out the documentation." : "Para obter máis detalles revise a documentación.",
"Database user" : "Usuario da base de datos",
"Database password" : "Contrasinal da base de datos",
diff --git a/core/l10n/gl.json b/core/l10n/gl.json
index ef523df2ead..165cf182549 100644
--- a/core/l10n/gl.json
+++ b/core/l10n/gl.json
@@ -341,7 +341,7 @@
"Data folder" : "Cartafol de datos",
"Configure the database" : "Configurar a base de datos",
"Only %s is available." : "Só está dispoñíbel %s.",
- "Install and activate additional PHP modules to choose other database types." : "Instale e active os módulos de PHP adicionais para seleccionar outros tipos de bases de datos.",
+ "Install and activate additional PHP modules to choose other database types." : "Instale e active os módulos de PHP adicionais para escoller outros tipos de bases de datos.",
"For more details check out the documentation." : "Para obter máis detalles revise a documentación.",
"Database user" : "Usuario da base de datos",
"Database password" : "Contrasinal da base de datos",
diff --git a/core/register_command.php b/core/register_command.php
index 32cd4099618..df39ad4484c 100644
--- a/core/register_command.php
+++ b/core/register_command.php
@@ -109,9 +109,9 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
$application->add(new OC\Core\Command\Db\ConvertType(\OC::$server->getConfig(), new \OC\DB\ConnectionFactory(\OC::$server->getSystemConfig())));
$application->add(new OC\Core\Command\Db\ConvertMysqlToMB4(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection(), \OC::$server->getURLGenerator(), \OC::$server->get(LoggerInterface::class)));
$application->add(new OC\Core\Command\Db\ConvertFilecacheBigInt(\OC::$server->get(\OC\DB\Connection::class)));
+ $application->add(\OCP\Server::get(\OC\Core\Command\Db\AddMissingColumns::class));
$application->add(\OCP\Server::get(\OC\Core\Command\Db\AddMissingIndices::class));
- $application->add(new OC\Core\Command\Db\AddMissingColumns(\OC::$server->get(\OC\DB\Connection::class), \OC::$server->getEventDispatcher()));
- $application->add(new OC\Core\Command\Db\AddMissingPrimaryKeys(\OC::$server->get(\OC\DB\Connection::class), \OC::$server->getEventDispatcher()));
+ $application->add(\OCP\Server::get(\OC\Core\Command\Db\AddMissingPrimaryKeys::class));
if (\OC::$server->getConfig()->getSystemValueBool('debug', false)) {
$application->add(new OC\Core\Command\Db\Migrations\StatusCommand(\OC::$server->get(\OC\DB\Connection::class)));
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index fcd1020be10..89ae83e83e4 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -210,7 +210,9 @@ return array(
'OCP\\Contacts\\ContactsMenu\\IProvider' => $baseDir . '/lib/public/Contacts/ContactsMenu/IProvider.php',
'OCP\\Contacts\\Events\\ContactInteractedWithEvent' => $baseDir . '/lib/public/Contacts/Events/ContactInteractedWithEvent.php',
'OCP\\Contacts\\IManager' => $baseDir . '/lib/public/Contacts/IManager.php',
+ 'OCP\\DB\\Events\\AddMissingColumnsEvent' => $baseDir . '/lib/public/DB/Events/AddMissingColumnsEvent.php',
'OCP\\DB\\Events\\AddMissingIndicesEvent' => $baseDir . '/lib/public/DB/Events/AddMissingIndicesEvent.php',
+ 'OCP\\DB\\Events\\AddMissingPrimaryKeyEvent' => $baseDir . '/lib/public/DB/Events/AddMissingPrimaryKeyEvent.php',
'OCP\\DB\\Exception' => $baseDir . '/lib/public/DB/Exception.php',
'OCP\\DB\\IPreparedStatement' => $baseDir . '/lib/public/DB/IPreparedStatement.php',
'OCP\\DB\\IResult' => $baseDir . '/lib/public/DB/IResult.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 783e63550c0..0480448a1b8 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -243,7 +243,9 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Contacts\\ContactsMenu\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IProvider.php',
'OCP\\Contacts\\Events\\ContactInteractedWithEvent' => __DIR__ . '/../../..' . '/lib/public/Contacts/Events/ContactInteractedWithEvent.php',
'OCP\\Contacts\\IManager' => __DIR__ . '/../../..' . '/lib/public/Contacts/IManager.php',
+ 'OCP\\DB\\Events\\AddMissingColumnsEvent' => __DIR__ . '/../../..' . '/lib/public/DB/Events/AddMissingColumnsEvent.php',
'OCP\\DB\\Events\\AddMissingIndicesEvent' => __DIR__ . '/../../..' . '/lib/public/DB/Events/AddMissingIndicesEvent.php',
+ 'OCP\\DB\\Events\\AddMissingPrimaryKeyEvent' => __DIR__ . '/../../..' . '/lib/public/DB/Events/AddMissingPrimaryKeyEvent.php',
'OCP\\DB\\Exception' => __DIR__ . '/../../..' . '/lib/public/DB/Exception.php',
'OCP\\DB\\IPreparedStatement' => __DIR__ . '/../../..' . '/lib/public/DB/IPreparedStatement.php',
'OCP\\DB\\IResult' => __DIR__ . '/../../..' . '/lib/public/DB/IResult.php',
diff --git a/lib/l10n/gl.js b/lib/l10n/gl.js
index d1967355c29..d70de0bb822 100644
--- a/lib/l10n/gl.js
+++ b/lib/l10n/gl.js
@@ -263,8 +263,7 @@ OC.L10N.register(
"Storage connection error. %s" : "Produciuse un erro na conexión ao almacenamento. %s",
"Storage is temporarily not available" : "O almacenamento non está dispoñíbel temporalmente",
"Storage connection timeout. %s" : "Esgotouse o tempo de conexión co almacenamento. %s",
- "Free prompt" : "Servizo de balde",
- "Runs an arbitrary prompt through the built-in language model." : "Executa unha instrucción arbitraria a través do modelo de linguaxe integrado.",
+ "Runs an arbitrary prompt through the built-in language model." : "Executa unha instrución arbitraria a través do modelo de linguaxe integrado.",
"Generate headline" : "Xerar título",
"Generates a possible headline for a text" : "Xera un posíbel título para un texto",
"Summarize" : "Resumir",
diff --git a/lib/l10n/gl.json b/lib/l10n/gl.json
index 4d0f4eb8d9a..bffaa04d080 100644
--- a/lib/l10n/gl.json
+++ b/lib/l10n/gl.json
@@ -261,8 +261,7 @@
"Storage connection error. %s" : "Produciuse un erro na conexión ao almacenamento. %s",
"Storage is temporarily not available" : "O almacenamento non está dispoñíbel temporalmente",
"Storage connection timeout. %s" : "Esgotouse o tempo de conexión co almacenamento. %s",
- "Free prompt" : "Servizo de balde",
- "Runs an arbitrary prompt through the built-in language model." : "Executa unha instrucción arbitraria a través do modelo de linguaxe integrado.",
+ "Runs an arbitrary prompt through the built-in language model." : "Executa unha instrución arbitraria a través do modelo de linguaxe integrado.",
"Generate headline" : "Xerar título",
"Generates a possible headline for a text" : "Xera un posíbel título para un texto",
"Summarize" : "Resumir",
diff --git a/lib/public/DB/Events/AddMissingColumnsEvent.php b/lib/public/DB/Events/AddMissingColumnsEvent.php
new file mode 100644
index 00000000000..1fb44e86842
--- /dev/null
+++ b/lib/public/DB/Events/AddMissingColumnsEvent.php
@@ -0,0 +1,60 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.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\DB\Events;
+
+/**
+ * Event to allow apps to register information about missing database columns
+ *
+ * This event will be dispatched for checking on the admin settings and when running
+ * occ db:add-missing-columns which will then create those columns
+ *
+ * @since 28.0.0
+ */
+class AddMissingColumnsEvent extends \OCP\EventDispatcher\Event {
+ /** @var array<array-key, array{tableName: string, columnName: string, typeName: string, options: array{}}> */
+ private array $missingColumns = [];
+
+ /**
+ * @param mixed[] $options
+ * @since 28.0.0
+ */
+ public function addMissingColumn(string $tableName, string $columnName, string $typeName, array $options): void {
+ $this->missingColumns[] = [
+ 'tableName' => $tableName,
+ 'columnName' => $columnName,
+ 'typeName' => $typeName,
+ 'options' => $options,
+ ];
+ }
+
+ /**
+ * @since 28.0.0
+ * @return array<array-key, array{tableName: string, columnName: string, typeName: string, options: array{}}>
+ */
+ public function getMissingColumns(): array {
+ return $this->missingColumns;
+ }
+}
diff --git a/lib/public/DB/Events/AddMissingIndicesEvent.php b/lib/public/DB/Events/AddMissingIndicesEvent.php
index 139b776b136..dc942f3d63e 100644
--- a/lib/public/DB/Events/AddMissingIndicesEvent.php
+++ b/lib/public/DB/Events/AddMissingIndicesEvent.php
@@ -2,9 +2,11 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2023 Julius Härtl <jus@bitgrid.net
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
+ * @copyright Copyright (c) 2023 Julius Härtl <jus@bitgrid.net>
*
- * @author Julius Härtl <jus@bitgrid.net
+ * @author Joas Schilling <coding@schilljs.com>
+ * @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
@@ -34,24 +36,41 @@ namespace OCP\DB\Events;
* @since 28.0.0
*/
class AddMissingIndicesEvent extends \OCP\EventDispatcher\Event {
- /** @var array<array-key, array{tableName: string, indexName: string, columns: string[]}> */
+ /** @var array<array-key, array{tableName: string, indexName: string, columns: string[], options: array{}, dropUnnamedIndex: bool, uniqueIndex: bool}> */
private array $missingIndices = [];
/**
* @param string[] $columns
* @since 28.0.0
*/
- public function addMissingIndex(string $tableName, string $indexName, array $columns): void {
+ public function addMissingIndex(string $tableName, string $indexName, array $columns, array $options = [], bool $dropUnnamedIndex = false): void {
$this->missingIndices[] = [
'tableName' => $tableName,
'indexName' => $indexName,
- 'columns' => $columns
+ 'columns' => $columns,
+ 'options' => $options,
+ 'dropUnnamedIndex' => $dropUnnamedIndex,
+ 'uniqueIndex' => false,
+ ];
+ }
+ /**
+ * @param string[] $columns
+ * @since 28.0.0
+ */
+ public function addMissingUniqueIndex(string $tableName, string $indexName, array $columns, array $options = [], bool $dropUnnamedIndex = false): void {
+ $this->missingIndices[] = [
+ 'tableName' => $tableName,
+ 'indexName' => $indexName,
+ 'columns' => $columns,
+ 'options' => $options,
+ 'dropUnnamedIndex' => $dropUnnamedIndex,
+ 'uniqueIndex' => true,
];
}
/**
* @since 28.0.0
- * @return array<array-key, array{tableName: string, indexName: string, columns: string[]}>
+ * @return array<array-key, array{tableName: string, indexName: string, columns: string[], options: array{}, dropUnnamedIndex: bool, uniqueIndex: bool}>
*/
public function getMissingIndices(): array {
return $this->missingIndices;
diff --git a/lib/public/DB/Events/AddMissingPrimaryKeyEvent.php b/lib/public/DB/Events/AddMissingPrimaryKeyEvent.php
new file mode 100644
index 00000000000..ace55d7538b
--- /dev/null
+++ b/lib/public/DB/Events/AddMissingPrimaryKeyEvent.php
@@ -0,0 +1,60 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2023 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @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\DB\Events;
+
+/**
+ * Event to allow apps to register information about missing database primary keys
+ *
+ * This event will be dispatched for checking on the admin settings and when running
+ * occ db:add-missing-primary-keys which will then create those keys
+ *
+ * @since 28.0.0
+ */
+class AddMissingPrimaryKeyEvent extends \OCP\EventDispatcher\Event {
+ /** @var array<array-key, array{tableName: string, primaryKeyName: string, columns: string[], formerIndex: null|string}> */
+ private array $missingPrimaryKeys = [];
+
+ /**
+ * @param string[] $columns
+ * @since 28.0.0
+ */
+ public function addMissingPrimaryKey(string $tableName, string $primaryKeyName, array $columns, ?string $formerIndex = null): void {
+ $this->missingPrimaryKeys[] = [
+ 'tableName' => $tableName,
+ 'primaryKeyName' => $primaryKeyName,
+ 'columns' => $columns,
+ 'formerIndex' => $formerIndex,
+ ];
+ }
+
+ /**
+ * @since 28.0.0
+ * @return array<array-key, array{tableName: string, primaryKeyName: string, columns: string[], formerIndex: null|string}>
+ */
+ public function getMissingPrimaryKeys(): array {
+ return $this->missingPrimaryKeys;
+ }
+}
diff --git a/lib/public/IDBConnection.php b/lib/public/IDBConnection.php
index bfc63b2aab0..fe0267facc5 100644
--- a/lib/public/IDBConnection.php
+++ b/lib/public/IDBConnection.php
@@ -34,7 +34,6 @@
namespace OCP;
use Doctrine\DBAL\Schema\Schema;
-use OCP\DB\Events\AddMissingIndicesEvent;
use OCP\DB\Exception;
use OCP\DB\IPreparedStatement;
use OCP\DB\IResult;
@@ -47,36 +46,6 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
*/
interface IDBConnection {
/**
- * @deprecated 22.0.0 this is an internal event, use {@see AddMissingIndicesEvent} instead
- */
- public const ADD_MISSING_INDEXES_EVENT = self::class . '::ADD_MISSING_INDEXES';
-
- /**
- * @deprecated 22.0.0 this is an internal event, use {@see AddMissingIndicesEvent} instead
- */
- public const CHECK_MISSING_INDEXES_EVENT = self::class . '::CHECK_MISSING_INDEXES';
-
- /**
- * @deprecated 22.0.0 this is an internal event
- */
- public const ADD_MISSING_PRIMARY_KEYS_EVENT = self::class . '::ADD_MISSING_PRIMARY_KEYS';
-
- /**
- * @deprecated 22.0.0 this is an internal event
- */
- public const CHECK_MISSING_PRIMARY_KEYS_EVENT = self::class . '::CHECK_MISSING_PRIMARY_KEYS';
-
- /**
- * @deprecated 22.0.0 this is an internal event
- */
- public const ADD_MISSING_COLUMNS_EVENT = self::class . '::ADD_MISSING_COLUMNS';
-
- /**
- * @deprecated 22.0.0 this is an internal event
- */
- public const CHECK_MISSING_COLUMNS_EVENT = self::class . '::CHECK_MISSING_COLUMNS';
-
- /**
* Gets the QueryBuilder for the connection.
*
* @return \OCP\DB\QueryBuilder\IQueryBuilder