Completed
Push — stable8.2 ( 3b3780...cb9d0b )
by Lukas
29s
created

RecipientPropagator   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 95.38%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 23
c 2
b 0
f 0
lcom 1
cbo 13
dl 0
loc 189
ccs 62
cts 65
cp 0.9538
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A propagateDirtyMountPoints() 0 13 4
A getDirtyShares() 0 17 4
B getPropagationTimestampForShares() 0 24 3
A markDirty() 0 6 2
A attachToPropagator() 0 5 1
B propagateById() 0 22 4
A isRecipientOfShare() 0 10 4
1
<?php
2
/**
3
 * @author Morris Jobke <[email protected]>
4
 * @author Robin Appelman <[email protected]>
5
 *
6
 * @copyright Copyright (c) 2015, ownCloud, Inc.
7
 * @license AGPL-3.0
8
 *
9
 * This code is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License, version 3,
11
 * as published by the Free Software Foundation.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License, version 3,
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
20
 *
21
 */
22
23
namespace OCA\Files_Sharing\Propagation;
24
25
use Doctrine\DBAL\Connection;
26
use OC\Files\Cache\ChangePropagator;
27
use OC\Files\View;
28
use OC\Share\Share;
29
use OCP\IGroupManager;
30
use OCP\IUser;
31
32
/**
33
 * Propagate etags for share recipients
34
 */
35
class RecipientPropagator {
36
	/**
37
	 * @var string
38
	 */
39
	protected $userId;
40
41
	/**
42
	 * @var \OC\Files\Cache\ChangePropagator
43
	 */
44
	protected $changePropagator;
45
46
	/**
47
	 * @var \OCP\IConfig
48
	 */
49
	protected $config;
50
51
	/**
52
	 * @var PropagationManager
53
	 */
54
	private $manager;
55
56
	/**
57
	 * @var IGroupManager
58
	 */
59
	private $groupManager;
60
61
	/**
62
	 * @var IUser
63
	 */
64
	private $user;
65
66
	/**
67
	 * @param IUser $user current user, must match the propagator's
68
	 * user
69
	 * @param \OC\Files\Cache\ChangePropagator $changePropagator change propagator
70
	 * initialized with a view for $user
71
	 * @param \OCP\IConfig $config
72
	 * @param PropagationManager $manager
73
	 * @param IGroupManager $groupManager
74 182
	 */
75 182
	public function __construct(IUser $user, $changePropagator, $config, PropagationManager $manager, IGroupManager $groupManager) {
76 182
		$this->userId = $user->getUID();
77 182
		$this->user = $user;
78 182
		$this->changePropagator = $changePropagator;
79 182
		$this->config = $config;
80 182
		$this->manager = $manager;
81 182
		$this->groupManager = $groupManager;
82
	}
83
84
	/**
85
	 * Propagate the etag changes for all shares marked as dirty and mark the shares as clean
86
	 *
87
	 * @param array $shares the shares for the users
88
	 * @param int $time
89 447
	 */
90 447
	public function propagateDirtyMountPoints(array $shares, $time = null) {
91 447
		if ($time === null) {
92 447
			$time = microtime(true);
93 447
		}
94 447
		$dirtyShares = $this->getDirtyShares($shares);
95 45
		foreach ($dirtyShares as $share) {
96 447
			$this->changePropagator->addChange($share['file_target']);
97 447
		}
98 45
		if (count($dirtyShares)) {
99 45
			$this->config->setUserValue($this->userId, 'files_sharing', 'last_propagate', $time);
100 45
			$this->changePropagator->propagateChanges(floor($time));
101 447
		}
102
	}
103
104
	/**
105
	 * Get all shares we need to update the etag for
106
	 *
107
	 * @param array $shares the shares for the users
108
	 * @return string[]
109 447
	 */
110 447
	protected function getDirtyShares($shares) {
111 447
		$dirty = [];
112 447
		$userTime = $this->config->getUserValue($this->userId, 'files_sharing', 'last_propagate', 0);
113 95
		$sharePropagations = [];
114 95
		foreach ($shares as $share) {
115 45
			$sharePropagations[(int) $share['id']] = 0;
116 45
		}
117 447
118 447
		$sharePropagations = $this->getPropagationTimestampForShares($sharePropagations);
119
120
		foreach ($shares as $share) {
121
			if ($sharePropagations[$share['id']] >= $userTime) {
122
				$dirty[] = $share;
123
			}
124
		}
125 31
		return $dirty;
126 31
	}
127
128
	/**
129 31
	 * Load the last dirty timestamp from the appconfig table
130 31
	 *
131
	 * @param int[] $sharePropagations
132
	 * @return int[]
133
	 */
134
	protected function getPropagationTimestampForShares(array $sharePropagations) {
135
		$sql = \OC::$server->getDatabaseConnection()->getQueryBuilder();
136
		$allShareIds = array_keys($sharePropagations);
137
138
		$shareIdChunks = array_chunk($allShareIds, 50);
139 269
140 52
		$sql->select(['configkey', 'configvalue'])
141 269
			->from('appconfig')
142 269
			->where($sql->expr()->eq('appid', $sql->createParameter('appid')))
143
			->andWhere($sql->expr()->in('configkey', $sql->createParameter('shareids')))
144
			->setParameter('appid', 'files_sharing', \PDO::PARAM_STR);
145
146 154
		foreach ($shareIdChunks as $shareIds) {
147 154
			$sql->setParameter('shareids', $shareIds, Connection::PARAM_INT_ARRAY);
148
			$result = $sql->execute();
149
150 154
			while ($row = $result->fetch()) {
151 154
				$sharePropagations[(int) $row['configkey']] = $row['configvalue'];
152 154
			}
153
			$result->closeCursor();
154 27
		}
155
156
		return $sharePropagations;
157 27
	}
158 18
159 18
	/**
160 18
	 * @param array $share
161 18
	 * @param float $time
162 18
	 */
163 18
	public function markDirty($share, $time = null) {
164 154
		if ($time === null) {
165
			$time = microtime(true);
166 154
		}
167 154
		$this->config->setAppValue('files_sharing', $share['id'], $time);
168
	}
169
170
	/**
171
	 * Listen on the propagator for updates made to shares owned by a user
172
	 *
173
	 * @param \OC\Files\Cache\ChangePropagator $propagator
174
	 * @param string $owner
175 27
	 */
176 27
	public function attachToPropagator(ChangePropagator $propagator, $owner) {
177 16
		$propagator->listen('\OC\Files', 'propagate', function ($path, $entry) use ($owner) {
178
			$this->propagateById($entry['fileid']);
179 27
		});
180 2
	}
181 2
182
	protected $propagatingIds = [];
183 25
184
	public function propagateById($id) {
185
		if (isset($this->propagatingIds[$id])) {
186
			return;
187
		}
188
		$this->propagatingIds[$id] = true;
189
		$shares = Share::getAllSharesForFileId($id);
190
		foreach ($shares as $share) {
191
			// propagate down the share tree
192
			$this->markDirty($share, microtime(true));
193
194
			// propagate up the share tree
195
			if ($this->isRecipientOfShare($share)) {
196
				$user = $share['uid_owner'];
197
				$view = new View('/' . $user . '/files');
198
				$path = $view->getPath($share['file_source']);
199
				$watcher = new ChangeWatcher($view, $this->manager->getSharePropagator($user));
200
				$watcher->writeHook(['path' => $path]);
201
			}
202
		}
203
204
		unset($this->propagatingIds[$id]);
205
	}
206
207
	/**
208
	 * Check if the user for this recipient propagator is the recipient of a share
209
	 *
210
	 * @param array $share
211
	 * @return bool
212
	 */
213
	private function isRecipientOfShare($share) {
214
		if ($share['share_with'] === $this->userId && $share['share_type'] == \OCP\Share::SHARE_TYPE_USER) { // == since 'share_type' is a string
215
			return true;
216
		}
217
		if ($share['share_type'] == \OCP\Share::SHARE_TYPE_GROUP) {
218
			$groups = $this->groupManager->getUserGroupIds($this->user);
219
			return in_array($share['share_with'], $groups);
220
		}
221
		return false;
222
	}
223
}
224