@@ -127,7 +127,7 @@ class Repair implements IOutput{ | |||
new RepairLegacyStorages(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection()), | |||
new AssetCache(), | |||
new FillETags(\OC::$server->getDatabaseConnection()), | |||
new CleanTags(\OC::$server->getDatabaseConnection()), | |||
new CleanTags(\OC::$server->getDatabaseConnection(), \OC::$server->getUserManager()), | |||
new DropOldTables(\OC::$server->getDatabaseConnection()), | |||
new DropOldJobs(\OC::$server->getJobList()), | |||
new RemoveGetETagEntries(\OC::$server->getDatabaseConnection()), |
@@ -25,6 +25,7 @@ namespace OC\Repair; | |||
use OCP\DB\QueryBuilder\IQueryBuilder; | |||
use OCP\IDBConnection; | |||
use OCP\IUserManager; | |||
use OCP\Migration\IOutput; | |||
use OCP\Migration\IRepairStep; | |||
@@ -38,11 +39,18 @@ class CleanTags implements IRepairStep { | |||
/** @var IDBConnection */ | |||
protected $connection; | |||
/** @var IUserManager */ | |||
protected $userManager; | |||
protected $deletedTags = 0; | |||
/** | |||
* @param IDBConnection $connection | |||
* @param IUserManager $userManager | |||
*/ | |||
public function __construct(IDBConnection $connection) { | |||
public function __construct(IDBConnection $connection, IUserManager $userManager) { | |||
$this->connection = $connection; | |||
$this->userManager = $userManager; | |||
} | |||
/** | |||
@@ -56,11 +64,58 @@ class CleanTags implements IRepairStep { | |||
* Updates the configuration after running an update | |||
*/ | |||
public function run(IOutput $output) { | |||
$this->deleteOrphanTags($output); | |||
$this->deleteOrphanFileEntries($output); | |||
$this->deleteOrphanTagEntries($output); | |||
$this->deleteOrphanCategoryEntries($output); | |||
} | |||
/** | |||
* Delete tags for deleted users | |||
*/ | |||
protected function deleteOrphanTags(IOutput $output) { | |||
$offset = 0; | |||
while ($this->checkTags($offset)) { | |||
$offset += 50; | |||
} | |||
$output->info(sprintf('%d tags of deleted users have been removed.', $this->deletedTags)); | |||
} | |||
protected function checkTags($offset) { | |||
$query = $this->connection->getQueryBuilder(); | |||
$query->select('uid') | |||
->from('vcategory') | |||
->groupBy('uid') | |||
->orderBy('uid') | |||
->setMaxResults(50) | |||
->setFirstResult($offset); | |||
$result = $query->execute(); | |||
$users = []; | |||
$hadResults = false; | |||
while ($row = $result->fetch()) { | |||
$hadResults = true; | |||
if (!$this->userManager->userExists($row['uid'])) { | |||
$users[] = $row['uid']; | |||
} | |||
} | |||
$result->closeCursor(); | |||
if (!$hadResults) { | |||
// No more tags, stop looping | |||
return false; | |||
} | |||
if (!empty($users)) { | |||
$query = $this->connection->getQueryBuilder(); | |||
$query->delete('vcategory') | |||
->where($query->expr()->in('uid', $query->createNamedParameter($users, IQueryBuilder::PARAM_STR_ARRAY))); | |||
$this->deletedTags += $query->execute(); | |||
} | |||
return true; | |||
} | |||
/** | |||
* Delete tag entries for deleted files | |||
*/ |
@@ -25,6 +25,9 @@ class CleanTagsTest extends \Test\TestCase { | |||
/** @var \OCP\IDBConnection */ | |||
protected $connection; | |||
/** @var \OCP\IUserManager|\PHPUnit_Framework_MockObject_MockObject */ | |||
protected $userManager; | |||
/** @var int */ | |||
protected $createdFile; | |||
@@ -38,8 +41,12 @@ class CleanTagsTest extends \Test\TestCase { | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$this->userManager = $this->getMockBuilder('\OCP\IUserManager') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$this->connection = \OC::$server->getDatabaseConnection(); | |||
$this->repair = new \OC\Repair\CleanTags($this->connection); | |||
$this->repair = new \OC\Repair\CleanTags($this->connection, $this->userManager); | |||
$this->cleanUpTables(); | |||
} | |||
@@ -86,6 +93,20 @@ class CleanTagsTest extends \Test\TestCase { | |||
self::invokePrivate($this->repair, 'deleteOrphanCategoryEntries', [$this->outputMock]); | |||
$this->assertEntryCount('vcategory_to_object', 2, 'Assert tag entries count after cleaning category entries'); | |||
$this->assertEntryCount('vcategory', 2, 'Assert tag categories count after cleaning category entries'); | |||
$this->addTagCategory('TestRepairCleanTags', 'contacts', 'userExists'); // Retained | |||
$this->assertEntryCount('vcategory', 3, 'Assert tag categories count before cleaning categories by users'); | |||
$this->userManager->expects($this->exactly(2)) | |||
->method('userExists') | |||
->willReturnMap([ | |||
['userExists', true], | |||
['TestRepairCleanTags', false], | |||
]); | |||
self::invokePrivate($this->repair, 'deleteOrphanTags', [$this->outputMock]); | |||
$this->assertEntryCount('vcategory', 1, 'Assert tag categories count after cleaning categories by users'); | |||
} | |||
/** | |||
@@ -107,13 +128,14 @@ class CleanTagsTest extends \Test\TestCase { | |||
* | |||
* @param string $category | |||
* @param string $type | |||
* @param string $user | |||
* @return int | |||
*/ | |||
protected function addTagCategory($category, $type) { | |||
protected function addTagCategory($category, $type, $user = 'TestRepairCleanTags') { | |||
$qb = $this->connection->getQueryBuilder(); | |||
$qb->insert('vcategory') | |||
->values([ | |||
'uid' => $qb->createNamedParameter('TestRepairCleanTags'), | |||
'uid' => $qb->createNamedParameter($user), | |||
'category' => $qb->createNamedParameter($category), | |||
'type' => $qb->createNamedParameter($type), | |||
]) |