diff options
author | Maxence Lange <maxence@artificial-owl.com> | 2021-11-12 15:39:07 +0300 |
---|---|---|
committer | backportbot[bot] <backportbot[bot]@users.noreply.github.com> | 2021-11-19 12:50:52 +0300 |
commit | 95baf3b28677975f5654414028db222e5b79f9fe (patch) | |
tree | 762fac40fe759016a293c2147c25ee8444310b8a | |
parent | 6a3689cc90cea28856b9639b5f70ef34633022d8 (diff) |
fix instancesbackport/843/stable21
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
-rw-r--r-- | appinfo/info.xml | 1 | ||||
-rw-r--r-- | lib/Command/FixInstance.php | 258 | ||||
-rw-r--r-- | lib/Db/MembersRequestBuilder.php | 8 |
3 files changed, 263 insertions, 4 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml index 1c3624d9..316c4fc3 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -75,6 +75,7 @@ Users won't be able to find this Circle using Nextcloud search engine. <command>OCA\Circles\Command\SyncContact</command> <!--<command>OCA\Circles\Command\Groups</command>--> <command>OCA\Circles\Command\FixUniqueId</command> + <command>OCA\Circles\Command\FixInstance</command> </commands> <settings> diff --git a/lib/Command/FixInstance.php b/lib/Command/FixInstance.php new file mode 100644 index 00000000..a251e7f7 --- /dev/null +++ b/lib/Command/FixInstance.php @@ -0,0 +1,258 @@ +<?php +/** + * Circles - Bring cloud-users closer together. + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2017 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Circles\Command; + +use OC\Core\Command\Base; +use OCA\Circles\Db\MembersRequest; +use OCA\Circles\Model\Member; +use OCP\IDBConnection; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; + + +/** + * + */ +class FixInstance extends Base { + + /** @var IDBConnection */ + protected $connection; + + /** @var MembersRequest */ + private $membersRequest; + + + /** @var InputInterface */ + private $input; + + /** @var OutputInterface */ + private $output; + + + /** + * @param MembersRequest $membersRequest + * @param IDBConnection $connection + */ + public function __construct( + MembersRequest $membersRequest, + IDBConnection $connection + ) { + parent::__construct(); + + $this->membersRequest = $membersRequest; + $this->connection = $connection; + } + + protected function configure() { + parent::configure(); + $this->setName('circles:fix:instance-alias') + ->setDescription('fix Instance aliases issue.') + ->addOption('fix', '', InputOption::VALUE_NONE, 'fix for real'); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $this->input = $input; + $this->output = $output; + $faulties = $this->getFaultyMembers(); + + $output->writeln('Found ' . sizeof($faulties) . ' faulty entries'); + + foreach ($faulties as $faulty) { + $output->writeln(''); + $output->writeln( + '> <info>' . $faulty->getUserId() . '</info> in <info>' . $faulty->getCircleId() + . '</info> with instance=<info>' . $faulty->getInstance() . '</info>' + ); + + $dupes = $this->getDuplicates($faulty); + if (sizeof($dupes) === 0) { + $this->fixInstance($faulty); + } else { + $this->deleteDupe($faulty, $dupes); + } + } + + return 0; + } + + + /** + * @return Member[] + */ + private function getFaultyMembers() { + $qb = $this->membersRequest->getMembersSelectSql(); + + $expr = $qb->expr(); + $qb->andWhere( + $expr->neq('instance', $qb->createNamedParameter('')), + $expr->neq($qb->createFunction('POSITION(\'@\' IN instance)'), $qb->createNamedParameter(0)) + ); + + $members = []; + $cursor = $qb->execute(); + while ($data = $cursor->fetch()) { + $members[] = $this->membersRequest->parseMembersSelectSql($data); + } + $cursor->closeCursor(); + + return $members; + } + + + /** + * @param Member $faulty + * + * @return array + * @throws \OCP\DB\Exception + */ + private function getDuplicates(Member $faulty) { + $qb = $this->membersRequest->getMembersSelectSql(); + + $expr = $qb->expr(); + $qb->andWhere( + $expr->neq('instance', $qb->createNamedParameter($faulty->getInstance())), + $expr->eq('circle_id', $qb->createNamedParameter($faulty->getCircleId())), + $expr->eq('user_type', $qb->createNamedParameter($faulty->getType())), + $expr->eq('user_id', $qb->createNamedParameter($faulty->getUserId())) + ); + + $dupes = []; + $cursor = $qb->execute(); + while ($data = $cursor->fetch()) { + $dupes[] = $this->membersRequest->parseMembersSelectSql($data); + } + $cursor->closeCursor(); + + return $dupes; + } + + + private function fixInstance(Member $faulty) { + [, $fixed] = explode('@', $faulty->getInstance(), 2); + $this->output->writeln(' - found no dupe, fixing instance to <info>' . $fixed . '</info>'); + + $question = new ConfirmationQuestion( + '<comment>Do you really want to ?</comment> (y/N) ', false, + '/^(y|Y)/i' + ); + + $helper = $this->getHelper('question'); + if (!$helper->ask($this->input, $this->output, $question)) { + $this->output->writeln('aborted.'); + + return; + } + + $qb = $this->membersRequest->getMembersUpdateSql( + $faulty->getCircleId(), + $faulty->getUserId(), + $faulty->getInstance(), + $faulty->getType() + ); + $qb->set('instance', $qb->createNamedParameter($fixed)); + + if ($this->input->getOption('fix')) { + $qb->execute(); + } + } + + + /** + * @param Member $faulty + * @param Member[] $dupes + */ + private function deleteDupe(Member $faulty, $dupes) { + if (sizeof($dupes) > 1) { + $this->output->writeln(' - <error>2 many dupes, please fix manually</error>'); + + return; + } + + $dupe = array_shift($dupes); + + $removeFaulty = false; + if ($dupe->getInstance() === '') { + $this->confirmDeleteDupe($faulty, $dupe); + + return; + } + + [, $fixed] = explode('@', $faulty->getInstance(), 2); + if ($dupe->getInstance() === $fixed) { + $this->confirmDeleteDupe($faulty, $dupe, $fixed); + + return; + } + + $this->output->writeln( + ' - <error>could not identify instance ' . $dupe->getInstance() . ', please fix manually</error>' + ); + } + + + private function confirmDeleteDupe(Member $faulty, Member $dupe, $fixed = '') { + if ($fixed === '') { + $msg = 'dupe is local'; + } else { + $msg = 'dupe instance is <info>' . $dupe->getInstance() . '</info>'; + } + + $this->output->writeln( + ' - ' . $msg . '. removing faulty with instance=<info>' . $faulty->getInstance() . '</info>' + ); + + $question = new ConfirmationQuestion( + '<comment>Do you really want to ?</comment> (y/N) ', false, + '/^(y|Y)/i' + ); + + $helper = $this->getHelper('question'); + if (!$helper->ask($this->input, $this->output, $question)) { + $this->output->writeln('aborted.'); + + return; + } + + $qb = $this->membersRequest->getMembersDeleteSql(); + $expr = $qb->expr(); + $qb->andWhere( + $expr->eq('instance', $qb->createNamedParameter($faulty->getInstance())), + $expr->eq('circle_id', $qb->createNamedParameter($faulty->getCircleId())), + $expr->eq('user_type', $qb->createNamedParameter($faulty->getType())), + $expr->eq('user_id', $qb->createNamedParameter($faulty->getUserId())) + ); + + if ($this->input->getOption('fix')) { + $qb->execute(); + } + } +} + + + diff --git a/lib/Db/MembersRequestBuilder.php b/lib/Db/MembersRequestBuilder.php index 3bef1323..faee4b19 100644 --- a/lib/Db/MembersRequestBuilder.php +++ b/lib/Db/MembersRequestBuilder.php @@ -76,7 +76,7 @@ class MembersRequestBuilder extends CoreRequestBuilder { /** * @return IQueryBuilder */ - protected function getMembersSelectSql() { + public function getMembersSelectSql() { $qb = $this->dbConnection->getQueryBuilder(); /** @noinspection PhpMethodParametersCountMismatchInspection */ @@ -103,7 +103,7 @@ class MembersRequestBuilder extends CoreRequestBuilder { * * @return IQueryBuilder */ - protected function getMembersUpdateSql(string $circleId, string $userId, string $instance, int $type) { + public function getMembersUpdateSql(string $circleId, string $userId, string $instance, int $type) { $qb = $this->dbConnection->getQueryBuilder(); $expr = $qb->expr(); @@ -127,7 +127,7 @@ class MembersRequestBuilder extends CoreRequestBuilder { * * @return IQueryBuilder */ - protected function getMembersDeleteSql() { + public function getMembersDeleteSql() { $qb = $this->dbConnection->getQueryBuilder(); $qb->delete(CoreRequestBuilder::TABLE_MEMBERS); @@ -140,7 +140,7 @@ class MembersRequestBuilder extends CoreRequestBuilder { * * @return Member */ - protected function parseMembersSelectSql(array $data) { + public function parseMembersSelectSql(array $data) { $member = new Member($data['user_id'], $data['user_type'], $data['circle_id']); $member->setNote($data['note']); $member->setContactId($data['contact_id']); |