SaveAccountsTableData.php 5.4 KB

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