diff options
author | Lukas Reschke <lukas@statuscode.ch> | 2017-05-18 22:31:36 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-18 22:31:36 +0200 |
commit | 10930c9ff29fb551d50a2dc29a61d18e61602d15 (patch) | |
tree | 7278a2f4d69f5ed6b2f212d65c52557b4b08b03d | |
parent | 879e11e7d1d9802647ade392f4ae317952bc6f8f (diff) | |
parent | 682a57d50e8ce3ef2307fe50eba9f91ff6537cc4 (diff) | |
download | nextcloud-server-10930c9ff29fb551d50a2dc29a61d18e61602d15.tar.gz nextcloud-server-10930c9ff29fb551d50a2dc29a61d18e61602d15.zip |
Merge pull request #4813 from nextcloud/accounts-table-migration-from-owncloud
Add a migration step to save the data from the accounts table before …
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 1 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 1 | ||||
-rw-r--r-- | lib/private/Repair.php | 3 | ||||
-rw-r--r-- | lib/private/Repair/NC11/MoveAvatarsBackgroundJob.php | 72 | ||||
-rw-r--r-- | lib/private/Repair/Owncloud/SaveAccountsTableData.php | 176 | ||||
-rw-r--r-- | lib/private/Updater.php | 19 | ||||
-rw-r--r-- | version.php | 1 |
7 files changed, 257 insertions, 16 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 10233f07a2a..8e65ca365e4 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -738,6 +738,7 @@ return array( 'OC\\Repair\\NC12\\InstallCoreBundle' => $baseDir . '/lib/private/Repair/NC12/InstallCoreBundle.php', 'OC\\Repair\\NC12\\UpdateLanguageCodes' => $baseDir . '/lib/private/Repair/NC12/UpdateLanguageCodes.php', 'OC\\Repair\\OldGroupMembershipShares' => $baseDir . '/lib/private/Repair/OldGroupMembershipShares.php', + 'OC\\Repair\\Owncloud\\SaveAccountsTableData' => $baseDir . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php', 'OC\\Repair\\RemoveRootShares' => $baseDir . '/lib/private/Repair/RemoveRootShares.php', 'OC\\Repair\\RepairInvalidShares' => $baseDir . '/lib/private/Repair/RepairInvalidShares.php', 'OC\\Repair\\RepairMimeTypes' => $baseDir . '/lib/private/Repair/RepairMimeTypes.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 9c0f0871133..b9a1545e6a3 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -768,6 +768,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Repair\\NC12\\InstallCoreBundle' => __DIR__ . '/../../..' . '/lib/private/Repair/NC12/InstallCoreBundle.php', 'OC\\Repair\\NC12\\UpdateLanguageCodes' => __DIR__ . '/../../..' . '/lib/private/Repair/NC12/UpdateLanguageCodes.php', 'OC\\Repair\\OldGroupMembershipShares' => __DIR__ . '/../../..' . '/lib/private/Repair/OldGroupMembershipShares.php', + 'OC\\Repair\\Owncloud\\SaveAccountsTableData' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php', 'OC\\Repair\\RemoveRootShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RemoveRootShares.php', 'OC\\Repair\\RepairInvalidShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RepairInvalidShares.php', 'OC\\Repair\\RepairMimeTypes' => __DIR__ . '/../../..' . '/lib/private/Repair/RepairMimeTypes.php', diff --git a/lib/private/Repair.php b/lib/private/Repair.php index 65e0342905a..4d14bf2550c 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -40,6 +40,7 @@ use OC\Repair\NC11\MoveAvatars; use OC\Repair\NC12\InstallCoreBundle; use OC\Repair\NC12\UpdateLanguageCodes; use OC\Repair\OldGroupMembershipShares; +use OC\Repair\Owncloud\SaveAccountsTableData; use OC\Repair\RemoveRootShares; use OC\Repair\SqliteAutoincrement; use OC\Repair\RepairMimeTypes; @@ -166,9 +167,11 @@ class Repair implements IOutput{ */ public static function getBeforeUpgradeRepairSteps() { $connection = \OC::$server->getDatabaseConnection(); + $config = \OC::$server->getConfig(); $steps = [ new Collation(\OC::$server->getConfig(), \OC::$server->getLogger(), $connection, true), new SqliteAutoincrement($connection), + new SaveAccountsTableData($connection, $config), ]; return $steps; diff --git a/lib/private/Repair/NC11/MoveAvatarsBackgroundJob.php b/lib/private/Repair/NC11/MoveAvatarsBackgroundJob.php index f8c0d9b3abf..d46b6fec8fc 100644 --- a/lib/private/Repair/NC11/MoveAvatarsBackgroundJob.php +++ b/lib/private/Repair/NC11/MoveAvatarsBackgroundJob.php @@ -28,6 +28,7 @@ use OCP\Files\Folder; use OCP\Files\IAppData; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; +use OCP\Files\SimpleFS\ISimpleFolder; use OCP\ILogger; use OCP\IUser; use OCP\IUserManager; @@ -63,8 +64,14 @@ class MoveAvatarsBackgroundJob extends QueuedJob { } private function moveAvatars() { + try { + $ownCloudAvatars = $this->rootFolder->get('avatars'); + } catch (NotFoundException $e) { + $ownCloudAvatars = null; + } + $counter = 0; - $this->userManager->callForSeenUsers(function (IUser $user) use ($counter) { + $this->userManager->callForSeenUsers(function (IUser $user) use ($counter, $ownCloudAvatars) { $uid = $user->getUID(); \OC\Files\Filesystem::initMountPoints($uid); @@ -77,26 +84,61 @@ class MoveAvatarsBackgroundJob extends QueuedJob { $userData = $this->appData->newFolder($uid); } + $foundAvatars = $this->copyAvatarsFromFolder($userFolder, $userData); - $regex = '/^avatar\.([0-9]+\.)?(jpg|png)$/'; - $avatars = $userFolder->getDirectoryListing(); - - foreach ($avatars as $avatar) { - /** @var File $avatar */ - if (preg_match($regex, $avatar->getName())) { - /* - * This is not the most effective but it is the most abstract way - * to handle this. Avatars should be small anyways. - */ - $newAvatar = $userData->newFile($avatar->getName()); - $newAvatar->putContent($avatar->getContent()); - $avatar->delete(); + // ownCloud migration? + if ($foundAvatars === 0 && $ownCloudAvatars instanceof Folder) { + $parts = $this->buildOwnCloudAvatarPath($uid); + $userOwnCloudAvatar = $ownCloudAvatars; + foreach ($parts as $part) { + try { + $userOwnCloudAvatar = $userOwnCloudAvatar->get($part); + } catch (NotFoundException $e) { + return; + } } + + $this->copyAvatarsFromFolder($userOwnCloudAvatar, $userData); } + $counter++; - if ($counter % 100) { + if ($counter % 100 === 0) { $this->logger->info('{amount} avatars migrated', ['amount' => $counter]); } }); } + + /** + * @param Folder $source + * @param ISimpleFolder $target + * @return int + * @throws \OCP\Files\NotPermittedException + * @throws NotFoundException + */ + protected function copyAvatarsFromFolder(Folder $source, ISimpleFolder $target) { + $foundAvatars = 0; + $avatars = $source->getDirectoryListing(); + $regex = '/^avatar\.([0-9]+\.)?(jpg|png)$/'; + + foreach ($avatars as $avatar) { + /** @var File $avatar */ + if (preg_match($regex, $avatar->getName())) { + /* + * This is not the most effective but it is the most abstract way + * to handle this. Avatars should be small anyways. + */ + $newAvatar = $target->newFile($avatar->getName()); + $newAvatar->putContent($avatar->getContent()); + $avatar->delete(); + $foundAvatars++; + } + } + + return $foundAvatars; + } + + protected function buildOwnCloudAvatarPath($userId) { + $avatar = substr_replace(substr_replace(md5($userId), '/', 4, 0), '/', 2, 0); + return explode('/', $avatar); + } } diff --git a/lib/private/Repair/Owncloud/SaveAccountsTableData.php b/lib/private/Repair/Owncloud/SaveAccountsTableData.php new file mode 100644 index 00000000000..35e5560856b --- /dev/null +++ b/lib/private/Repair/Owncloud/SaveAccountsTableData.php @@ -0,0 +1,176 @@ +<?php +/** + * @copyright Copyright (c) 2017 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\Owncloud; + +use Doctrine\DBAL\Exception\InvalidFieldNameException; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; +use OCP\PreConditionNotMetException; + +/** + * Copies the email address from the accounts table to the preference table, + * before the data structure is changed and the information is gone + */ +class SaveAccountsTableData implements IRepairStep { + + const BATCH_SIZE = 75; + + /** @var IDBConnection */ + protected $db; + + /** @var IConfig */ + protected $config; + + /** + * @param IDBConnection $db + * @param IConfig $config + */ + public function __construct(IDBConnection $db, IConfig $config) { + $this->db = $db; + $this->config = $config; + } + + /** + * @return string + */ + public function getName() { + return 'Copy data from accounts table when migrating from ownCloud'; + } + + /** + * @param IOutput $output + */ + public function run(IOutput $output) { + if (!$this->shouldRun()) { + return; + } + + $offset = 0; + $numUsers = $this->runStep($offset); + + while ($numUsers === self::BATCH_SIZE) { + $offset += $numUsers; + $numUsers = $this->runStep($offset); + } + + // Remove the table + $this->db->dropTable('accounts'); + } + + /** + * @return bool + */ + protected function shouldRun() { + $query = $this->db->getQueryBuilder(); + $query->select('*') + ->from('accounts') + ->where($query->expr()->isNotNull('user_id')) + ->setMaxResults(1); + + try { + $query->execute(); + return true; + } catch (InvalidFieldNameException $e) { + return false; + } + } + + /** + * @param int $offset + * @return int Number of copied users + */ + protected function runStep($offset) { + $query = $this->db->getQueryBuilder(); + $query->select('*') + ->from('accounts') + ->orderBy('id') + ->setMaxResults(self::BATCH_SIZE); + + if ($offset > 0) { + $query->setFirstResult($offset); + } + + $result = $query->execute(); + + $update = $this->db->getQueryBuilder(); + $update->update('users') + ->set('displayname', $update->createParameter('displayname')) + ->where($update->expr()->eq('uid', $update->createParameter('userid'))); + + $updatedUsers = 0; + while ($row = $result->fetch()) { + try { + $this->migrateUserInfo($update, $row); + } catch (PreConditionNotMetException $e) { + // Ignore and continue + } catch (\UnexpectedValueException $e) { + // Ignore and continue + } + $updatedUsers++; + } + $result->closeCursor(); + + return $updatedUsers; + } + + /** + * @param IQueryBuilder $update + * @param array $userdata + * @throws PreConditionNotMetException + * @throws \UnexpectedValueException + */ + protected function migrateUserInfo(IQueryBuilder $update, $userdata) { + $state = (int) $userdata['state']; + if ($state === 3) { + // Deleted user, ignore + return; + } + + if ($userdata['email'] !== null) { + $this->config->setUserValue($userdata['user_id'], 'settings', 'email', $userdata['email']); + } + if ($userdata['quota'] !== null) { + $this->config->setUserValue($userdata['user_id'], 'files', 'quota', $userdata['quota']); + } + if ($userdata['last_login'] !== null) { + $this->config->setUserValue($userdata['user_id'], 'login', 'lastLogin', $userdata['last_login']); + } + if ($state === 1) { + $this->config->setUserValue($userdata['user_id'], 'core', 'enabled', 'true'); + } else if ($state === 2) { + $this->config->setUserValue($userdata['user_id'], 'core', 'enabled', 'false'); + } + + if ($userdata['display_name'] !== null) { + $update->setParameter('displayname', $userdata['display_name']) + ->setParameter('userid', $userdata['user_id']); + $update->execute(); + } + + } +} + diff --git a/lib/private/Updater.php b/lib/private/Updater.php index 6f81e6175f3..6d08e5d4cc0 100644 --- a/lib/private/Updater.php +++ b/lib/private/Updater.php @@ -190,6 +190,22 @@ class Updater extends BasicEmitter { $majorMinor = $version[0] . '.' . $version[1]; $currentVendor = $this->config->getAppValue('core', 'vendor', ''); + + // Vendor was not set correctly on install, so we have to white-list known versions + if ($currentVendor === '') { + if (in_array($oldVersion, [ + '11.0.2.7', + '11.0.1.2', + '11.0.0.10', + ], true)) { + $currentVendor = 'nextcloud'; + } else if (in_array($oldVersion, [ + '10.0.0.12', + ], true)) { + $currentVendor = 'owncloud'; + } + } + if ($currentVendor === 'nextcloud') { return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) && (version_compare($oldVersion, $newVersion, '<=') || @@ -197,7 +213,8 @@ class Updater extends BasicEmitter { } // Check if the instance can be migrated - return isset($allowedPreviousVersions[$currentVendor][$majorMinor]); + return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) || + isset($allowedPreviousVersions[$currentVendor][$oldVersion]); } /** diff --git a/version.php b/version.php index 37e15f35e1f..8673939044f 100644 --- a/version.php +++ b/version.php @@ -37,6 +37,7 @@ $OC_VersionCanBeUpgradedFrom = [ '12.0' => true, ], 'owncloud' => [ + '10.0.0.12' => true, ], ]; |