diff options
Diffstat (limited to 'lib/private/Repair')
-rw-r--r-- | lib/private/Repair/NC20/EncryptionLegacyCipher.php | 3 | ||||
-rw-r--r-- | lib/private/Repair/NC20/EncryptionMigration.php | 3 | ||||
-rw-r--r-- | lib/private/Repair/NC21/ValidatePhoneNumber.php | 89 | ||||
-rw-r--r-- | lib/private/Repair/Owncloud/CleanPreviews.php | 73 | ||||
-rw-r--r-- | lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php | 132 | ||||
-rw-r--r-- | lib/private/Repair/Owncloud/InstallCoreBundle.php | 80 | ||||
-rw-r--r-- | lib/private/Repair/Owncloud/MoveAvatars.php | 73 | ||||
-rw-r--r-- | lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php | 116 | ||||
-rw-r--r-- | lib/private/Repair/Owncloud/SaveAccountsTableData.php | 9 | ||||
-rw-r--r-- | lib/private/Repair/Owncloud/UpdateLanguageCodes.php | 90 |
10 files changed, 666 insertions, 2 deletions
diff --git a/lib/private/Repair/NC20/EncryptionLegacyCipher.php b/lib/private/Repair/NC20/EncryptionLegacyCipher.php index 0be34679fcb..f887feefc98 100644 --- a/lib/private/Repair/NC20/EncryptionLegacyCipher.php +++ b/lib/private/Repair/NC20/EncryptionLegacyCipher.php @@ -58,7 +58,8 @@ class EncryptionLegacyCipher implements IRepairStep { return; } - if ($this->manager->isEnabled()) { + $masterKeyId = $this->config->getAppValue('encryption', 'masterKeyId'); + if ($this->manager->isEnabled() || !empty($masterKeyId)) { if ($this->config->getSystemValue('encryption.legacy_format_support', '') === '') { $this->config->setSystemValue('encryption.legacy_format_support', true); } diff --git a/lib/private/Repair/NC20/EncryptionMigration.php b/lib/private/Repair/NC20/EncryptionMigration.php index 1eba09f413a..d5879073d59 100644 --- a/lib/private/Repair/NC20/EncryptionMigration.php +++ b/lib/private/Repair/NC20/EncryptionMigration.php @@ -58,7 +58,8 @@ class EncryptionMigration implements IRepairStep { return; } - if ($this->manager->isEnabled()) { + $masterKeyId = $this->config->getAppValue('encryption', 'masterKeyId'); + if ($this->manager->isEnabled() || !empty($masterKeyId)) { if ($this->config->getSystemValue('encryption.key_storage_migrated', '') === '') { $this->config->setSystemValue('encryption.key_storage_migrated', false); } diff --git a/lib/private/Repair/NC21/ValidatePhoneNumber.php b/lib/private/Repair/NC21/ValidatePhoneNumber.php new file mode 100644 index 00000000000..6e25ff26b7e --- /dev/null +++ b/lib/private/Repair/NC21/ValidatePhoneNumber.php @@ -0,0 +1,89 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 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 OC\Repair\NC21; + +use OC\Accounts\AccountManager; +use OCP\Accounts\IAccountManager; +use OCP\IConfig; +use OCP\IUser; +use OCP\IUserManager; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class ValidatePhoneNumber implements IRepairStep { + + /** @var IConfig */ + protected $config; + /** @var IUserManager */ + protected $userManager; + /** @var AccountManager */ + private $accountManager; + + public function __construct(IUserManager $userManager, + AccountManager $accountManager, + IConfig $config) { + $this->config = $config; + $this->userManager = $userManager; + $this->accountManager = $accountManager; + } + + public function getName(): string { + return 'Validate the phone number and store it in a known format for search'; + } + + private function shouldRun(): bool { + return true; + } + + public function run(IOutput $output): void { + if ($this->config->getSystemValueString('default_phone_region', '') === '') { + throw new \Exception('Can not validate phone numbers without `default_phone_region` being set in the config file'); + } + + $numUpdated = 0; + $numRemoved = 0; + + $this->userManager->callForSeenUsers(function (IUser $user) use (&$numUpdated, &$numRemoved) { + $account = $this->accountManager->getUser($user); + + if ($account[IAccountManager::PROPERTY_PHONE]['value'] !== '') { + $updated = $this->accountManager->updateUser($user, $account); + + if ($account[IAccountManager::PROPERTY_PHONE]['value'] !== $updated[IAccountManager::PROPERTY_PHONE]['value']) { + if ($updated[IAccountManager::PROPERTY_PHONE]['value'] === '') { + $numRemoved++; + } else { + $numUpdated++; + } + } + } + }); + + if ($numRemoved > 0 || $numUpdated > 0) { + $output->info('Updated ' . $numUpdated . ' entries and cleaned ' . $numRemoved . ' invalid phone numbers'); + } + } +} diff --git a/lib/private/Repair/Owncloud/CleanPreviews.php b/lib/private/Repair/Owncloud/CleanPreviews.php new file mode 100644 index 00000000000..5c183451d67 --- /dev/null +++ b/lib/private/Repair/Owncloud/CleanPreviews.php @@ -0,0 +1,73 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OC\Repair\Owncloud; + +use OCP\BackgroundJob\IJobList; +use OCP\IConfig; +use OCP\IUser; +use OCP\IUserManager; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class CleanPreviews implements IRepairStep { + + /** @var IJobList */ + private $jobList; + + /** @var IUserManager */ + private $userManager; + + /** @var IConfig */ + private $config; + + /** + * MoveAvatars constructor. + * + * @param IJobList $jobList + * @param IUserManager $userManager + * @param IConfig $config + */ + public function __construct(IJobList $jobList, + IUserManager $userManager, + IConfig $config) { + $this->jobList = $jobList; + $this->userManager = $userManager; + $this->config = $config; + } + + /** + * @return string + */ + public function getName() { + return 'Add preview cleanup background jobs'; + } + + public function run(IOutput $output) { + if (!$this->config->getAppValue('core', 'previewsCleanedUp', false)) { + $this->userManager->callForSeenUsers(function (IUser $user) { + $this->jobList->add(CleanPreviewsBackgroundJob::class, ['uid' => $user->getUID()]); + }); + $this->config->setAppValue('core', 'previewsCleanedUp', '1'); + } + } +} diff --git a/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php b/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php new file mode 100644 index 00000000000..e8d89c9c7a0 --- /dev/null +++ b/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php @@ -0,0 +1,132 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OC\Repair\Owncloud; + +use OC\BackgroundJob\QueuedJob; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJobList; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\ILogger; +use OCP\IUserManager; + +class CleanPreviewsBackgroundJob extends QueuedJob { + /** @var IRootFolder */ + private $rootFolder; + + /** @var ILogger */ + private $logger; + + /** @var IJobList */ + private $jobList; + + /** @var ITimeFactory */ + private $timeFactory; + + /** @var IUserManager */ + private $userManager; + + /** + * CleanPreviewsBackgroundJob constructor. + * + * @param IRootFolder $rootFolder + * @param ILogger $logger + * @param IJobList $jobList + * @param ITimeFactory $timeFactory + * @param IUserManager $userManager + */ + public function __construct(IRootFolder $rootFolder, + ILogger $logger, + IJobList $jobList, + ITimeFactory $timeFactory, + IUserManager $userManager) { + $this->rootFolder = $rootFolder; + $this->logger = $logger; + $this->jobList = $jobList; + $this->timeFactory = $timeFactory; + $this->userManager = $userManager; + } + + public function run($arguments) { + $uid = $arguments['uid']; + if (!$this->userManager->userExists($uid)) { + $this->logger->info('User no longer exists, skip user ' . $uid); + return; + } + $this->logger->info('Started preview cleanup for ' . $uid); + $empty = $this->cleanupPreviews($uid); + + if (!$empty) { + $this->jobList->add(self::class, ['uid' => $uid]); + $this->logger->info('New preview cleanup scheduled for ' . $uid); + } else { + $this->logger->info('Preview cleanup done for ' . $uid); + } + } + + /** + * @param $uid + * @return bool + */ + private function cleanupPreviews($uid) { + try { + $userFolder = $this->rootFolder->getUserFolder($uid); + } catch (NotFoundException $e) { + return true; + } + + $userRoot = $userFolder->getParent(); + + try { + /** @var Folder $thumbnailFolder */ + $thumbnailFolder = $userRoot->get('thumbnails'); + } catch (NotFoundException $e) { + return true; + } + + $thumbnails = $thumbnailFolder->getDirectoryListing(); + + $start = $this->timeFactory->getTime(); + foreach ($thumbnails as $thumbnail) { + try { + $thumbnail->delete(); + } catch (NotPermittedException $e) { + // Ignore + } + + if (($this->timeFactory->getTime() - $start) > 15) { + return false; + } + } + + try { + $thumbnailFolder->delete(); + } catch (NotPermittedException $e) { + // Ignore + } + + return true; + } +} diff --git a/lib/private/Repair/Owncloud/InstallCoreBundle.php b/lib/private/Repair/Owncloud/InstallCoreBundle.php new file mode 100644 index 00000000000..6d07ec9b962 --- /dev/null +++ b/lib/private/Repair/Owncloud/InstallCoreBundle.php @@ -0,0 +1,80 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @author Lukas Reschke <lukas@statuscode.ch> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Repair\Owncloud; + +use OC\App\AppStore\Bundles\BundleFetcher; +use OC\Installer; +use OCP\IConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class InstallCoreBundle implements IRepairStep { + /** @var BundleFetcher */ + private $bundleFetcher; + /** @var IConfig */ + private $config; + /** @var Installer */ + private $installer; + + /** + * @param BundleFetcher $bundleFetcher + * @param IConfig $config + * @param Installer $installer + */ + public function __construct(BundleFetcher $bundleFetcher, + IConfig $config, + Installer $installer) { + $this->bundleFetcher = $bundleFetcher; + $this->config = $config; + $this->installer = $installer; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'Install new core bundle components'; + } + + /** + * {@inheritdoc} + */ + public function run(IOutput $output) { + $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); + + if (version_compare($versionFromBeforeUpdate, '12.0.0.14', '>')) { + return; + } + + $defaultBundle = $this->bundleFetcher->getDefaultInstallationBundle(); + foreach ($defaultBundle as $bundle) { + try { + $this->installer->installAppBundle($bundle); + $output->info('Successfully installed core app bundle.'); + } catch (\Exception $e) { + $output->warning('Could not install core app bundle: ' . $e->getMessage()); + } + } + } +} diff --git a/lib/private/Repair/Owncloud/MoveAvatars.php b/lib/private/Repair/Owncloud/MoveAvatars.php new file mode 100644 index 00000000000..53f3097aeec --- /dev/null +++ b/lib/private/Repair/Owncloud/MoveAvatars.php @@ -0,0 +1,73 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Morris Jobke <hey@morrisjobke.de> + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OC\Repair\Owncloud; + +use OCP\BackgroundJob\IJobList; +use OCP\IConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class MoveAvatars implements IRepairStep { + + /** @var IJobList */ + private $jobList; + + /** @var IConfig */ + private $config; + + /** + * MoveAvatars constructor. + * + * @param IJobList $jobList + * @param IConfig $config + */ + public function __construct(IJobList $jobList, + IConfig $config) { + $this->jobList = $jobList; + $this->config = $config; + } + + /** + * @return string + */ + public function getName() { + return 'Add move avatar background job'; + } + + public function run(IOutput $output) { + // only run once + if ($this->config->getAppValue('core', 'moveavatarsdone') === 'yes') { + $output->info('Repair step already executed'); + return; + } + if ($this->config->getSystemValue('enable_avatars', true) === false) { + $output->info('Avatars are disabled'); + } else { + $output->info('Add background job'); + $this->jobList->add(MoveAvatarsBackgroundJob::class); + // if all were done, no need to redo the repair during next upgrade + $this->config->setAppValue('core', 'moveavatarsdone', 'yes'); + } + } +} diff --git a/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php b/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php new file mode 100644 index 00000000000..ec076d116c5 --- /dev/null +++ b/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php @@ -0,0 +1,116 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Joas Schilling <coding@schilljs.com> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OC\Repair\Owncloud; + +use OC\BackgroundJob\QueuedJob; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\Files\Storage; +use OCP\IAvatarManager; +use OCP\IUser; +use OCP\IUserManager; +use Psr\Log\LoggerInterface; + +class MoveAvatarsBackgroundJob extends QueuedJob { + + /** @var IUserManager */ + private $userManager; + + /** @var LoggerInterface */ + private $logger; + + /** @var IAvatarManager */ + private $avatarManager; + + /** @var Storage */ + private $owncloudAvatarStorage; + + public function __construct(IUserManager $userManager, LoggerInterface $logger, IAvatarManager $avatarManager, IRootFolder $rootFolder) { + $this->userManager = $userManager; + $this->logger = $logger; + $this->avatarManager = $avatarManager; + try { + $this->owncloudAvatarStorage = $rootFolder->get('avatars')->getStorage(); + } catch (\Exception $e) { + } + } + + public function run($arguments) { + $this->logger->info('Started migrating avatars to AppData folder'); + $this->moveAvatars(); + $this->logger->info('All avatars migrated to AppData folder'); + } + + private function moveAvatars(): void { + if (!$this->owncloudAvatarStorage) { + $this->logger->info('No legacy avatars available, skipping migration'); + return; + } + + $counter = 0; + $this->userManager->callForSeenUsers(function (IUser $user) use ($counter) { + $uid = $user->getUID(); + + $path = 'avatars/' . $this->buildOwnCloudAvatarPath($uid); + $avatar = $this->avatarManager->getAvatar($uid); + try { + $avatarPath = $path . '/avatar.' . $this->getExtension($path); + $resource = $this->owncloudAvatarStorage->fopen($avatarPath, 'r'); + if ($resource) { + $avatar->set($resource); + fclose($resource); + } else { + throw new \Exception('Failed to open old avatar file for reading'); + } + } catch (NotFoundException $e) { + // In case there is no avatar we can just skip + } catch (\Throwable $e) { + $this->logger->error('Failed to migrate avatar for user ' . $uid, ['exception' => $e]); + } + + $counter++; + if ($counter % 100 === 0) { + $this->logger->info('{amount} avatars migrated', ['amount' => $counter]); + } + }); + } + + /** + * @throws NotFoundException + */ + private function getExtension(string $path): string { + if ($this->owncloudAvatarStorage->file_exists("{$path}/avatar.jpg")) { + return 'jpg'; + } + if ($this->owncloudAvatarStorage->file_exists("{$path}/avatar.png")) { + return 'png'; + } + throw new NotFoundException("{$path}/avatar.jpg|png"); + } + + protected function buildOwnCloudAvatarPath(string $userId): string { + return substr_replace(substr_replace(md5($userId), '/', 4, 0), '/', 2, 0); + } +} diff --git a/lib/private/Repair/Owncloud/SaveAccountsTableData.php b/lib/private/Repair/Owncloud/SaveAccountsTableData.php index 6ca46934d71..89c331a7d71 100644 --- a/lib/private/Repair/Owncloud/SaveAccountsTableData.php +++ b/lib/private/Repair/Owncloud/SaveAccountsTableData.php @@ -78,6 +78,15 @@ class SaveAccountsTableData implements IRepairStep { $numUsers = $this->runStep($offset); } + // oc_persistent_locks will be removed later on anyways so we can just drop and ignore any foreign key constraints here + $tableName = $this->config->getSystemValue('dbtableprefix', 'oc_') . 'persistent_locks'; + $schema = $this->db->createSchema(); + $table = $schema->getTable($tableName); + foreach ($table->getForeignKeys() as $foreignKey) { + $table->removeForeignKey($foreignKey->getName()); + } + $this->db->migrateToSchema($schema); + // Remove the table if ($this->hasForeignKeyOnPersistentLocks) { $this->db->dropTable('persistent_locks'); diff --git a/lib/private/Repair/Owncloud/UpdateLanguageCodes.php b/lib/private/Repair/Owncloud/UpdateLanguageCodes.php new file mode 100644 index 00000000000..b7da0b00528 --- /dev/null +++ b/lib/private/Repair/Owncloud/UpdateLanguageCodes.php @@ -0,0 +1,90 @@ +<?php +/** + * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> + * + * @author Joas Schilling <coding@schilljs.com> + * @author Morris Jobke <hey@morrisjobke.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Repair\Owncloud; + +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class UpdateLanguageCodes implements IRepairStep { + /** @var IDBConnection */ + private $connection; + + /** @var IConfig */ + private $config; + + /** + * @param IDBConnection $connection + * @param IConfig $config + */ + public function __construct(IDBConnection $connection, + IConfig $config) { + $this->connection = $connection; + $this->config = $config; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'Repair language codes'; + } + + /** + * {@inheritdoc} + */ + public function run(IOutput $output) { + $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); + + if (version_compare($versionFromBeforeUpdate, '12.0.0.13', '>')) { + return; + } + + $languages = [ + 'bg_BG' => 'bg', + 'cs_CZ' => 'cs', + 'fi_FI' => 'fi', + 'hu_HU' => 'hu', + 'nb_NO' => 'nb', + 'sk_SK' => 'sk', + 'th_TH' => 'th', + ]; + + foreach ($languages as $oldCode => $newCode) { + $qb = $this->connection->getQueryBuilder(); + + $affectedRows = $qb->update('preferences') + ->set('configvalue', $qb->createNamedParameter($newCode)) + ->where($qb->expr()->eq('appid', $qb->createNamedParameter('core'))) + ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter('lang'))) + ->andWhere($qb->expr()->eq('configvalue', $qb->createNamedParameter($oldCode), IQueryBuilder::PARAM_STR)) + ->execute(); + + $output->info('Changed ' . $affectedRows . ' setting(s) from "' . $oldCode . '" to "' . $newCode . '" in preferences table.'); + } + } +} |