Passed
Push — master ( cfdece...554c78 )
by Morris
12:32
created

MoveCalendar::execute()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 31
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 16
nc 5
nop 2
dl 0
loc 31
rs 9.4222
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Thomas Citharel <[email protected]>
4
 *
5
 * @license AGPL-3.0
6
 *
7
 * This code is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License, version 3,
9
 * as published by the Free Software Foundation.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License, version 3,
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
18
 *
19
 */
20
namespace OCA\DAV\Command;
21
22
use OCA\DAV\CalDAV\CalDavBackend;
23
use OCA\DAV\CalDAV\Calendar;
24
use OCA\DAV\Connector\Sabre\Principal;
25
use OCP\IConfig;
26
use OCP\IDBConnection;
27
use OCP\IGroupManager;
28
use OCP\IL10N;
29
use OCP\IUserManager;
30
use OCP\IUserSession;
31
use OCP\Share\IManager as IShareManager;
32
use Symfony\Component\Console\Command\Command;
33
use Symfony\Component\Console\Input\InputArgument;
34
use Symfony\Component\Console\Input\InputInterface;
35
use Symfony\Component\Console\Input\InputOption;
36
use Symfony\Component\Console\Output\OutputInterface;
37
use Symfony\Component\Console\Style\SymfonyStyle;
38
39
class MoveCalendar extends Command {
40
41
	/** @var IUserManager */
42
	private $userManager;
43
44
	/** @var IGroupManager */
45
	private $groupManager;
46
47
	/** @var IShareManager */
48
	private $shareManager;
49
50
	/** @var IConfig $config */
51
	private $config;
52
53
	/** @var IL10N */
54
	private $l10n;
55
56
	/** @var SymfonyStyle */
57
	private $io;
58
59
	/** @var CalDavBackend */
60
	private $calDav;
61
62
	const URI_USERS = 'principals/users/';
63
64
	/**
65
	 * @param IUserManager $userManager
66
	 * @param IGroupManager $groupManager
67
	 * @param IShareManager $shareManager
68
	 * @param IConfig $config
69
	 * @param IL10N $l10n
70
	 * @param CalDavBackend $calDav
71
	 */
72
	function __construct(
73
		IUserManager $userManager,
74
		IGroupManager $groupManager,
75
		IShareManager $shareManager,
76
		IConfig $config,
77
		IL10N $l10n,
78
		CalDavBackend $calDav
79
	) {
80
		parent::__construct();
81
		$this->userManager = $userManager;
82
		$this->groupManager = $groupManager;
83
		$this->shareManager = $shareManager;
84
		$this->config = $config;
85
		$this->l10n = $l10n;
86
		$this->calDav = $calDav;
87
	}
88
89
	protected function configure() {
90
		$this
91
			->setName('dav:move-calendar')
92
			->setDescription('Move a calendar from an user to another')
93
			->addArgument('name',
94
				InputArgument::REQUIRED,
95
				'Name of the calendar to move')
96
			->addArgument('sourceuid',
97
				InputArgument::REQUIRED,
98
				'User who currently owns the calendar')
99
			->addArgument('destinationuid',
100
				InputArgument::REQUIRED,
101
				'User who will receive the calendar')
102
			->addOption('force', 'f', InputOption::VALUE_NONE, "Force the migration by removing existing shares");
103
	}
104
105
	protected function execute(InputInterface $input, OutputInterface $output) {
106
		$userOrigin = $input->getArgument('sourceuid');
107
		$userDestination = $input->getArgument('destinationuid');
108
109
		$this->io = new SymfonyStyle($input, $output);
110
111
		if (!$this->userManager->userExists($userOrigin)) {
0 ignored issues
show
Bug introduced by
It seems like $userOrigin can also be of type string[]; however, parameter $uid of OCP\IUserManager::userExists() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

111
		if (!$this->userManager->userExists(/** @scrutinizer ignore-type */ $userOrigin)) {
Loading history...
112
			throw new \InvalidArgumentException("User <$userOrigin> is unknown.");
113
		}
114
115
		if (!$this->userManager->userExists($userDestination)) {
116
			throw new \InvalidArgumentException("User <$userDestination> is unknown.");
117
		}
118
119
		$name = $input->getArgument('name');
120
121
		$calendar = $this->calDav->getCalendarByUri(self::URI_USERS . $userOrigin, $name);
0 ignored issues
show
Bug introduced by
Are you sure $userOrigin of type null|string|string[] can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

121
		$calendar = $this->calDav->getCalendarByUri(self::URI_USERS . /** @scrutinizer ignore-type */ $userOrigin, $name);
Loading history...
Bug introduced by
Are you sure the assignment to $calendar is correct as $this->calDav->getCalend...S . $userOrigin, $name) targeting OCA\DAV\CalDAV\CalDavBackend::getCalendarByUri() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
It seems like $name can also be of type string[]; however, parameter $uri of OCA\DAV\CalDAV\CalDavBackend::getCalendarByUri() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

121
		$calendar = $this->calDav->getCalendarByUri(self::URI_USERS . $userOrigin, /** @scrutinizer ignore-type */ $name);
Loading history...
122
123
		if (null === $calendar) {
0 ignored issues
show
introduced by
The condition null === $calendar is always true.
Loading history...
124
			throw new \InvalidArgumentException("User <$userOrigin> has no calendar named <$name>. You can run occ dav:list-calendars to list calendars URIs for this user.");
125
		}
126
127
		if (null !== $this->calDav->getCalendarByUri(self::URI_USERS . $userDestination, $name)) {
128
			throw new \InvalidArgumentException("User <$userDestination> already has a calendar named <$name>.");
129
		}
130
131
		$this->checkShares($calendar, $userOrigin, $userDestination, $input->getOption('force'));
132
133
		$this->calDav->moveCalendar($name, self::URI_USERS . $userOrigin, self::URI_USERS . $userDestination);
134
135
		$this->io->success("Calendar <$name> was moved from user <$userOrigin> to <$userDestination>");
136
	}
137
138
	/**
139
	 * Check that moving the calendar won't break shares
140
	 *
141
	 * @param array $calendar
142
	 * @param string $userOrigin
143
	 * @param string $userDestination
144
	 * @param bool $force
145
	 */
146
	private function checkShares(array $calendar, string $userOrigin, string $userDestination, bool $force = false)
147
	{
148
		$shares = $this->calDav->getShares($calendar['id']);
149
		foreach ($shares as $share) {
150
			list(, $prefix, $userOrGroup) = explode('/', $share['href'], 3);
151
152
			/**
153
			 * Check that user destination is member of the groups which whom the calendar was shared
154
			 * If we ask to force the migration, the share with the group is dropped
155
			 */
156
			if ($this->shareManager->shareWithGroupMembersOnly() === true && 'groups' === $prefix && !$this->groupManager->isInGroup($userDestination, $userOrGroup)) {
157
				if ($force) {
158
					$this->calDav->updateShares(new Calendar($this->calDav, $calendar, $this->l10n, $this->config), [], ['href' => 'principal:principals/groups/' . $userOrGroup]);
159
				} else {
160
					throw new \InvalidArgumentException("User <$userDestination> is not part of the group <$userOrGroup> with whom the calendar <" . $calendar['uri'] . "> was shared. You may use -f to move the calendar while deleting this share.");
161
				}
162
			}
163
164
			/**
165
			 * Check that calendar isn't already shared with user destination
166
			 */
167
			if ($userOrGroup === $userDestination) {
168
				if ($force) {
169
					$this->calDav->updateShares(new Calendar($this->calDav, $calendar, $this->l10n, $this->config), [], ['href' => 'principal:principals/users/' . $userOrGroup]);
170
				} else {
171
					throw new \InvalidArgumentException("The calendar <" . $calendar['uri'] . "> is already shared to user <$userDestination>.You may use -f to move the calendar while deleting this share.");
172
				}
173
			}
174
		}
175
		/**
176
		 * Warn that share links have changed if there are shares
177
		 */
178
		if (count($shares) > 0) {
179
			$this->io->note([
180
				"Please note that moving calendar " . $calendar['uri'] . " from user <$userOrigin> to <$userDestination> has caused share links to change.", 
181
				"Sharees will need to change \"example.com/remote.php/dav/calendars/uid/" . $calendar['uri'] . "_shared_by_$userOrigin\" to \"example.com/remote.php/dav/calendars/uid/" . $calendar['uri'] . "_shared_by_$userDestination\""
182
			]);
183
		}
184
	}
185
}
186