Repository navigation
upgrade to sharing 2.0, remove hierarchical re-shares #21660
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,6 @@ | ||
| <?php | ||
| /** | ||
| * @author Björn Schießle <schiessle@owncloud.com> | ||
| * @author Morris Jobke <hey@morrisjobke.de> | ||
| * | ||
| * @copyright Copyright (c) 2016, ownCloud, Inc. | ||
| * @license AGPL-3.0 | ||
|
|
@@ -20,22 +19,246 @@ | |
| * | ||
| */ | ||
|
|
||
| namespace OCA\Files_Sharing; | ||
| namespace OCA\Files_Sharing; | ||
|
|
||
| use Doctrine\DBAL\Connection; | ||
| use OCP\IDBConnection; | ||
| use OC\Cache\CappedMemoryCache; | ||
|
|
||
| /** | ||
| * Class Migration | ||
| * | ||
| * @package OCA\Files_Sharing | ||
| * @group DB | ||
| */ | ||
| class Migration { | ||
|
|
||
| /** @var IDBConnection */ | ||
| private $connection; | ||
|
|
||
| /** @var array with all shares we already saw */ | ||
| private $shareCache; | ||
|
|
||
| /** @var string */ | ||
| private $table = 'share'; | ||
|
|
||
| public function __construct(IDBConnection $connection) { | ||
| $this->connection = $connection; | ||
|
|
||
| // We cache up to 10k share items (~20MB) | ||
| $this->shareCache = new CappedMemoryCache(10000); | ||
| } | ||
|
|
||
| /** | ||
| * move all re-shares to the owner in order to have a flat list of shares | ||
| * upgrade from oC 8.2 to 9.0 with the new sharing | ||
| */ | ||
| public function removeReShares() { | ||
|
|
||
| while(true) { | ||
| $reShares = $this->getReShares(1000); | ||
|
|
||
| if (empty($reShares)) { | ||
| break; | ||
| } | ||
|
|
||
| // Update the cache | ||
| foreach($reShares as $reShare) { | ||
| $this->shareCache[$reShare['id']] = $reShare; | ||
| } | ||
|
|
||
| $owners = []; | ||
| foreach ($reShares as $share) { | ||
| $owners[$share['id']] = [ | ||
| 'owner' => $this->findOwner($share), | ||
| 'initiator' => $share['uid_owner'] | ||
| ]; | ||
| } | ||
| $this->updateOwners($owners); | ||
|
|
||
| //Clear the cache of the shares we just updated so we have more room | ||
| foreach($owners as $id => $owner) { | ||
| unset($this->shareCache[$id]); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * set accepted to 1 for all external shares. At this point in time we only | ||
| * have shares from the first version of server-to-server sharing so all should | ||
| * be accepted | ||
| * update all owner information so that all shares have an owner | ||
| * and an initiator for the upgrade from oC 8.2 to 9.0 with the new sharing | ||
| */ | ||
| public function addAcceptRow() { | ||
| $statement = 'UPDATE `*PREFIX*share_external` SET `accepted` = 1'; | ||
| $connection = \OC::$server->getDatabaseConnection(); | ||
| $query = $connection->prepare($statement); | ||
| $query->execute(); | ||
| public function updateInitiatorInfo() { | ||
| while (true) { | ||
| $shares = $this->getMissingInitiator(1000); | ||
|
|
||
| if (empty($shares)) { | ||
| break; | ||
| } | ||
|
|
||
| $owners = []; | ||
| foreach ($shares as $share) { | ||
| $owners[$share['id']] = [ | ||
| 'owner' => $share['uid_owner'], | ||
| 'initiator' => $share['uid_owner'] | ||
| ]; | ||
| } | ||
| $this->updateOwners($owners); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * find the owner of a re-shared file/folder | ||
| * | ||
| * @param array $share | ||
| * @return array | ||
| */ | ||
| private function findOwner($share) { | ||
| $currentShare = $share; | ||
| while(!is_null($currentShare['parent'])) { | ||
| if (isset($this->shareCache[$currentShare['parent']])) { | ||
| $currentShare = $this->shareCache[$currentShare['parent']]; | ||
| } else { | ||
| $currentShare = $this->getShare((int)$currentShare['parent']); | ||
| $this->shareCache[$currentShare['id']] = $currentShare; | ||
| } | ||
| } | ||
|
|
||
| return $currentShare['uid_owner']; | ||
| } | ||
|
|
||
| /** | ||
| * Get $n re-shares from the database | ||
| * | ||
| * @param int $n The max number of shares to fetch | ||
| * @return array | ||
| */ | ||
| private function getReShares($n = 1000) { | ||
| $query = $this->connection->getQueryBuilder(); | ||
| $query->select(['id', 'parent', 'uid_owner']) | ||
| ->from($this->table) | ||
| ->where($query->expr()->in( | ||
| 'share_type', | ||
| $query->createNamedParameter( | ||
| [ | ||
| \OCP\Share::SHARE_TYPE_USER, | ||
| \OCP\Share::SHARE_TYPE_GROUP, | ||
| \OCP\Share::SHARE_TYPE_LINK | ||
| ], | ||
| Connection::PARAM_INT_ARRAY | ||
| ) | ||
| )) | ||
| ->andWhere($query->expr()->in( | ||
| 'item_type', | ||
| $query->createNamedParameter( | ||
| ['file', 'folder'], | ||
| Connection::PARAM_STR_ARRAY | ||
| ) | ||
| )) | ||
| ->andWhere($query->expr()->isNotNull('parent')) | ||
| ->orderBy('id', 'asc') | ||
| ->setMaxResults($n); | ||
| $result = $query->execute(); | ||
| $shares = $result->fetchAll(); | ||
| $result->closeCursor(); | ||
|
|
||
| $ordered = []; | ||
| foreach ($shares as $share) { | ||
| $ordered[(int)$share['id']] = $share; | ||
| } | ||
|
|
||
| return $ordered; | ||
| } | ||
|
|
||
| /** | ||
| * Get $n re-shares from the database | ||
| * | ||
| * @param int $n The max number of shares to fetch | ||
| * @return array | ||
| */ | ||
| private function getMissingInitiator($n = 1000) { | ||
| $query = $this->connection->getQueryBuilder(); | ||
| $query->select(['id', 'uid_owner']) | ||
| ->from($this->table) | ||
| ->where($query->expr()->in( | ||
| 'share_type', | ||
| $query->createNamedParameter( | ||
| [ | ||
| \OCP\Share::SHARE_TYPE_USER, | ||
| \OCP\Share::SHARE_TYPE_GROUP, | ||
| \OCP\Share::SHARE_TYPE_LINK | ||
| ], | ||
| Connection::PARAM_INT_ARRAY | ||
| ) | ||
| )) | ||
| ->andWhere($query->expr()->in( | ||
| 'item_type', | ||
| $query->createNamedParameter( | ||
| ['file', 'folder'], | ||
| Connection::PARAM_STR_ARRAY | ||
| ) | ||
| )) | ||
| ->andWhere($query->expr()->isNull('uid_initiator')) | ||
| ->orderBy('id', 'asc') | ||
| ->setMaxResults($n); | ||
| $result = $query->execute(); | ||
| $shares = $result->fetchAll(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking about big installations this can be quite a lot. An iterativ approach would be better.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While I think even for big installations the number of reshares is still manageable. I do agree that iterative approach would be safer. |
||
| $result->closeCursor(); | ||
|
|
||
| $ordered = []; | ||
| foreach ($shares as $share) { | ||
| $ordered[(int)$share['id']] = $share; | ||
| } | ||
|
|
||
| return $ordered; | ||
| } | ||
|
|
||
| /** | ||
| * get a specific share | ||
| * | ||
| * @param int $id | ||
| * @return array | ||
| */ | ||
| private function getShare($id) { | ||
| $query = $this->connection->getQueryBuilder(); | ||
| $query->select(['id', 'parent', 'uid_owner']) | ||
| ->from($this->table) | ||
| ->where($query->expr()->eq('id', $query->createNamedParameter($id))); | ||
| $result = $query->execute(); | ||
| $share = $result->fetchAll(); | ||
| $result->closeCursor(); | ||
|
|
||
| return $share[0]; | ||
| } | ||
|
|
||
| /** | ||
| * update database with the new owners | ||
| * | ||
| * @param array $owners | ||
| * @throws \Exception | ||
| */ | ||
| private function updateOwners($owners) { | ||
|
|
||
| $this->connection->beginTransaction(); | ||
|
|
||
| try { | ||
|
|
||
| foreach ($owners as $id => $owner) { | ||
| $query = $this->connection->getQueryBuilder(); | ||
| $query->update($this->table) | ||
| ->set('parent', $query->createNamedParameter(null)) | ||
| ->set('uid_owner', $query->createNamedParameter($owner['owner'])) | ||
| ->set('uid_initiator', $query->createNamedParameter($owner['initiator'])) | ||
| ->where($query->expr()->eq('id', $query->createNamedParameter($id))) | ||
| ->execute(); | ||
| } | ||
|
|
||
| $this->connection->commit(); | ||
|
|
||
| } catch (\Exception $e) { | ||
| $this->connection->rollBack(); | ||
| throw $e; | ||
| } | ||
|
|
||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing you can fix here. But it is kinda weird this needs to be included here. Breaks the abstraction