bookmarks/lib/Migration/OrphanedTreeItemsRepairStep...

211 lines
7.4 KiB
PHP

<?php
/*
* Copyright (c) 2020-2024. The Nextcloud Bookmarks contributors.
*
* This file is licensed under the Affero General Public License version 3 or later. See the COPYING file.
*/
namespace OCA\Bookmarks\Migration;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class OrphanedTreeItemsRepairStep implements IRepairStep {
/**
* @var IDBConnection
*/
private $db;
public function __construct(IDBConnection $db) {
$this->db = $db;
}
/**
* * Returns the step's name
*
* @return string
*/
public function getName() {
return 'Remove orphaned bookmark tree items';
}
/**
* @param IOutput $output
*
* @return void
*/
public function run(IOutput $output) {
$qb = $this->db->getQueryBuilder();
$qb->select('t.id')
->from('bookmarks_tree', 't')
->leftJoin('t', 'bookmarks', 'b', $qb->expr()->eq('b.id', 't.id'))
->where($qb->expr()->isNull('b.id'))
->andWhere($qb->expr()->eq('t.type', $qb->createPositionalParameter('bookmark')));
$orphanedBookmarks = $qb->execute();
$i = 0;
while ($bookmark = $orphanedBookmarks->fetchColumn()) {
$qb = $this->db->getQueryBuilder();
$qb->delete('bookmarks_tree')
->where($qb->expr()->eq('id', $qb->createPositionalParameter($bookmark, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('type', $qb->createPositionalParameter('bookmark')))
->execute();
$i++;
}
$output->info("Removed $i orphaned bookmarks entries");
$qb = $this->db->getQueryBuilder();
$qb->select('t.id')
->from('bookmarks_tree', 't')
->leftJoin('t', 'bookmarks_folders', 'f', $qb->expr()->eq('f.id', 't.id'))
->leftJoin('t', 'bookmarks_root_folders', 'r', $qb->expr()->eq('t.id', 'r.folder_id'))
->where($qb->expr()->isNull('f.id'))
->andWhere($qb->expr()->isNull('r.folder_id'))
->andWhere($qb->expr()->eq('t.type', $qb->createPositionalParameter('folder')));
$orphanedFolders = $qb->execute();
$i = 0;
while ($folder = $orphanedFolders->fetchColumn()) {
$qb = $this->db->getQueryBuilder();
$qb->delete('bookmarks_tree')
->where($qb->expr()->eq('id', $qb->createPositionalParameter($folder, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('type', $qb->createPositionalParameter('folder')))
->execute();
$i++;
}
$output->info("Removed $i orphaned folders entries");
$qb = $this->db->getQueryBuilder();
$qb->select('t.id', 't.type')
->from('bookmarks_tree', 't')
->leftJoin('t', 'bookmarks_folders', 'f', $qb->expr()->eq('t.parent_folder', 'f.id'))
->where($qb->expr()->isNull('f.id'));
$orphanedTreeItems = $qb->execute();
$i = 0;
while ($treeItem = $orphanedTreeItems->fetch()) {
if ($treeItem['type'] === 'bookmark') {
$qb = $this->db->getQueryBuilder();
$bookmark = $qb->select('user_id')
->from('bookmarks')
->where($qb->expr()->eq('id', $qb->createNamedParameter($treeItem['id'])))
->execute()
->fetch();
$userId = $bookmark['user_id'];
} elseif ($treeItem['type'] === 'folder') {
$qb = $this->db->getQueryBuilder();
$folder = $qb->select('user_id')
->from('bookmarks_folders')
->where($qb->expr()->eq('id', $qb->createNamedParameter($treeItem['id'])))
->execute()
->fetch();
$userId = $folder['user_id'];
} elseif ($treeItem['type'] === 'share') {
$qb = $this->db->getQueryBuilder();
$folder = $qb->select('user_id')
->from('bookmarks_shared_folders')
->where($qb->expr()->eq('id', $qb->createNamedParameter($treeItem['id'])))
->execute()
->fetch();
$userId = $folder['user_id'];
}
$rootFolder = $this->ensureRootFolder($userId);
$qb = $this->db->getQueryBuilder();
$qb->update('bookmarks_tree')
->set('parent_folder', $qb->createNamedParameter($rootFolder['folder_id']))
->set('index', $qb->createNamedParameter($rootFolder['count']))
->where($qb->expr()->eq('id', $qb->createNamedParameter($treeItem['id'], IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('type', $qb->createNamedParameter($treeItem['type'])))
->execute();
$i++;
}
$output->info("Reinserted $i orphaned children entries");
$qb = $this->db->getQueryBuilder();
$qb->select('f.id')
->from('bookmarks_folders', 'f')
->leftJoin('f', 'bookmarks_tree', 't', $qb->expr()->eq('t.id', 'f.id'))
->leftJoin('f', 'bookmarks_root_folders', 'r', $qb->expr()->eq('r.folder_id', 'f.id'))
->where($qb->expr()->isNull('t.id'))
->andWhere($qb->expr()->isNull('r.folder_id'))
->andWhere($qb->expr()->eq('t.type', $qb->createPositionalParameter('folder')));
$orphanedFolders = $qb->execute();
$i = 0;
while ($folder = $orphanedFolders->fetchColumn()) {
$qb = $this->db->getQueryBuilder();
$qb->delete('bookmarks_folders')
->where($qb->expr()->eq('id', $qb->createPositionalParameter($folder, IQueryBuilder::PARAM_INT)))
->execute();
$i++;
}
$output->info("Removed $i orphaned bookmark folders");
$qb = $this->db->getQueryBuilder();
$qb->select('b.id', 'b.user_id')
->from('bookmarks', 'b')
->leftJoin('b', 'bookmarks_tree', 't', 'b.id = t.id AND t.type = '.$qb->createPositionalParameter('bookmark'))
->where($qb->expr()->isNull('t.id'));
$orphanedBookmarks = $qb->execute();
$i = 0;
while ($bookmark = $orphanedBookmarks->fetch()) {
$rootFolder = $this->ensureRootFolder($bookmark['user_id']);
$qb = $this->db->getQueryBuilder();
$qb->insert('bookmarks_tree')->values([
'id' => $qb->createPositionalParameter($bookmark['id']),
'type' => $qb->createPositionalParameter('bookmark'),
'parent_folder' => $qb->createPositionalParameter($rootFolder['folder_id']),
'index' => $qb->createPositionalParameter($rootFolder['count']),
])->execute();
$i++;
}
$output->info("Reinserted $i orphaned bookmarks");
}
private function ensureRootFolder($userId) {
$qb = $this->db->getQueryBuilder();
$rootFolder = $qb->select('r.folder_id', $qb->func()->count('t.id', 'count'))
->from('bookmarks_root_folders', 'r')
->innerJoin('r', 'bookmarks_folders', 'f', 'r.folder_id = f.id')
->leftJoin('r', 'bookmarks_tree', 't', 't.parent_folder = r.folder_id')
->where($qb->expr()->eq('r.user_id', $qb->createNamedParameter($userId)))
->groupBy(['r.folder_id'])
->execute()
->fetch();
if ($rootFolder === null || $rootFolder === false || $rootFolder['folder_id'] === null) {
$qb = $this->db->getQueryBuilder();
$qb->insert('bookmarks_folders')
->values([
'user_id' => $qb->createNamedParameter($userId),
'title' => $qb->createNamedParameter('')
])
->execute();
$rootFolder = [
'folder_id' => $qb->getLastInsertId(),
'count' => 0
];
$qb = $this->db->getQueryBuilder();
$oldRootFolder = $qb->select('folder_id')
->from('bookmarks_root_folders')
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)))
->execute()
->fetch();
if ($oldRootFolder) {
$qb = $this->db->getQueryBuilder();
$qb->update('bookmarks_root_folders')
->set('folder_id', $qb->createNamedParameter($rootFolder['folder_id']))
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)))
->execute();
} else {
$qb = $this->db->getQueryBuilder();
$qb->insert('bookmarks_root_folders')
->values([
'folder_id' => $qb->createNamedParameter($rootFolder['folder_id']),
'user_id' => $qb->createNamedParameter($userId)
])
->execute();
}
}
return $rootFolder;
}
}