You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

190 lines
4.9 KiB

3 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
  4. *
  5. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  6. * @author Joas Schilling <coding@schilljs.com>
  7. *
  8. * @license GNU AGPL version 3 or any later version
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Affero General Public License as
  12. * published by the Free Software Foundation, either version 3 of the
  13. * License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Affero General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. *
  23. */
  24. namespace OC\Repair\Owncloud;
  25. use OCP\DB\QueryBuilder\IQueryBuilder;
  26. use OCP\IConfig;
  27. use OCP\IDBConnection;
  28. use OCP\Migration\IOutput;
  29. use OCP\Migration\IRepairStep;
  30. use OCP\PreConditionNotMetException;
  31. /**
  32. * Copies the email address from the accounts table to the preference table,
  33. * before the data structure is changed and the information is gone
  34. */
  35. class SaveAccountsTableData implements IRepairStep {
  36. public const BATCH_SIZE = 75;
  37. /** @var IDBConnection */
  38. protected $db;
  39. /** @var IConfig */
  40. protected $config;
  41. protected $hasForeignKeyOnPersistentLocks = false;
  42. /**
  43. * @param IDBConnection $db
  44. * @param IConfig $config
  45. */
  46. public function __construct(IDBConnection $db, IConfig $config) {
  47. $this->db = $db;
  48. $this->config = $config;
  49. }
  50. /**
  51. * @return string
  52. */
  53. public function getName() {
  54. return 'Copy data from accounts table when migrating from ownCloud';
  55. }
  56. /**
  57. * @param IOutput $output
  58. */
  59. public function run(IOutput $output) {
  60. if (!$this->shouldRun()) {
  61. return;
  62. }
  63. $offset = 0;
  64. $numUsers = $this->runStep($offset);
  65. while ($numUsers === self::BATCH_SIZE) {
  66. $offset += $numUsers;
  67. $numUsers = $this->runStep($offset);
  68. }
  69. // Remove the table
  70. if ($this->hasForeignKeyOnPersistentLocks) {
  71. $this->db->dropTable('persistent_locks');
  72. }
  73. $this->db->dropTable('accounts');
  74. }
  75. /**
  76. * @return bool
  77. */
  78. protected function shouldRun() {
  79. $schema = $this->db->createSchema();
  80. $prefix = $this->config->getSystemValue('dbtableprefix', 'oc_');
  81. $tableName = $prefix . 'accounts';
  82. if (!$schema->hasTable($tableName)) {
  83. return false;
  84. }
  85. $table = $schema->getTable($tableName);
  86. if (!$table->hasColumn('user_id')) {
  87. return false;
  88. }
  89. if ($schema->hasTable($prefix . 'persistent_locks')) {
  90. $locksTable = $schema->getTable($prefix . 'persistent_locks');
  91. $foreignKeys = $locksTable->getForeignKeys();
  92. foreach ($foreignKeys as $foreignKey) {
  93. if ($tableName === $foreignKey->getForeignTableName()) {
  94. $this->hasForeignKeyOnPersistentLocks = true;
  95. }
  96. }
  97. }
  98. return true;
  99. }
  100. /**
  101. * @param int $offset
  102. * @return int Number of copied users
  103. */
  104. protected function runStep($offset) {
  105. $query = $this->db->getQueryBuilder();
  106. $query->select('*')
  107. ->from('accounts')
  108. ->orderBy('id')
  109. ->setMaxResults(self::BATCH_SIZE);
  110. if ($offset > 0) {
  111. $query->setFirstResult($offset);
  112. }
  113. $result = $query->execute();
  114. $update = $this->db->getQueryBuilder();
  115. $update->update('users')
  116. ->set('displayname', $update->createParameter('displayname'))
  117. ->where($update->expr()->eq('uid', $update->createParameter('userid')));
  118. $updatedUsers = 0;
  119. while ($row = $result->fetch()) {
  120. try {
  121. $this->migrateUserInfo($update, $row);
  122. } catch (PreConditionNotMetException $e) {
  123. // Ignore and continue
  124. } catch (\UnexpectedValueException $e) {
  125. // Ignore and continue
  126. }
  127. $updatedUsers++;
  128. }
  129. $result->closeCursor();
  130. return $updatedUsers;
  131. }
  132. /**
  133. * @param IQueryBuilder $update
  134. * @param array $userdata
  135. * @throws PreConditionNotMetException
  136. * @throws \UnexpectedValueException
  137. */
  138. protected function migrateUserInfo(IQueryBuilder $update, $userdata) {
  139. $state = (int) $userdata['state'];
  140. if ($state === 3) {
  141. // Deleted user, ignore
  142. return;
  143. }
  144. if ($userdata['email'] !== null) {
  145. $this->config->setUserValue($userdata['user_id'], 'settings', 'email', $userdata['email']);
  146. }
  147. if ($userdata['quota'] !== null) {
  148. $this->config->setUserValue($userdata['user_id'], 'files', 'quota', $userdata['quota']);
  149. }
  150. if ($userdata['last_login'] !== null) {
  151. $this->config->setUserValue($userdata['user_id'], 'login', 'lastLogin', $userdata['last_login']);
  152. }
  153. if ($state === 1) {
  154. $this->config->setUserValue($userdata['user_id'], 'core', 'enabled', 'true');
  155. } elseif ($state === 2) {
  156. $this->config->setUserValue($userdata['user_id'], 'core', 'enabled', 'false');
  157. }
  158. if ($userdata['display_name'] !== null) {
  159. $update->setParameter('displayname', $userdata['display_name'])
  160. ->setParameter('userid', $userdata['user_id']);
  161. $update->execute();
  162. }
  163. }
  164. }