Completed
Push — master ( 24957e...d8c031 )
by Thomas
16:29 queued 07:32
created

AbstractCustomPropertiesBackend::offsetGet()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Thomas Müller <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2017, ownCloud GmbH
6
 * @license AGPL-3.0
7
 *
8
 * This code is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License, version 3,
10
 * as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License, version 3,
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19
 *
20
 */
21
22
namespace OCA\DAV\DAV;
23
24
use OCP\IDBConnection;
25
use OCP\IUser;
26
use Sabre\DAV\INode;
27
use Sabre\DAV\PropertyStorage\Backend\BackendInterface;
28
use Sabre\DAV\PropFind;
29
use Sabre\DAV\PropPatch;
30
use Sabre\DAV\Tree;
31
use Sabre\Dav\Exception\Forbidden;
32
use Sabre\DAV\Exception\NotFound;
33
use Sabre\DAV\Exception\ServiceUnavailable;
34
35
abstract class AbstractCustomPropertiesBackend implements BackendInterface {
36
37
	/**
38
	 * Ignored properties
39
	 *
40
	 * @var array
41
	 */
42
	protected $ignoredProperties = [
43
		'{DAV:}getcontentlength',
44
		'{DAV:}getcontenttype',
45
		'{DAV:}getetag',
46
		'{DAV:}quota-used-bytes',
47
		'{DAV:}quota-available-bytes',
48
		'{http://owncloud.org/ns}permissions',
49
		'{http://owncloud.org/ns}downloadURL',
50
		'{http://owncloud.org/ns}dDC',
51
		'{http://owncloud.org/ns}size',
52
	];
53
54
	/**
55
	 * @var Tree
56
	 */
57
	protected $tree;
58
59
	/**
60
	 * @var IDBConnection
61
	 */
62
	protected $connection;
63
64
	/**
65
	 * @var string
66
	 */
67
	protected $user;
68
69
	/**
70
	 * Property cache for the filesystem items
71
	 * @var array
72
	 */
73
	protected $cache;
74
75
	/**
76
	 * @param Tree $tree node tree
77
	 * @param IDBConnection $connection database connection
78
	 * @param IUser $user owner of the tree and properties
79
	 */
80
	public function __construct(
81
		Tree $tree,
82
		IDBConnection $connection,
83
		IUser $user) {
84
		$this->tree = $tree;
85
		$this->connection = $connection;
86
		$this->user = $user->getUID();
87
	}
88
89
	/**
90
	 * Returns a list of properties for these nodes
91
	 *
92
	 * @param string $path path
93
	 * @param INode $node
94
	 * @param array $requestedProperties requested properties or empty array for "all"
95
	 * @return array
96
	 * @note The properties list is a list of propertynames the client
97
	 * requested, encoded as xmlnamespace#tagName, for example:
98
	 * http://www.example.org/namespace#author If the array is empty, all
99
	 * properties should be returned
100
	 */
101
	abstract protected function getProperties($path, INode $node, array $requestedProperties);
102
103
	/**
104
	 * Update properties
105
	 *
106
	 * @param string $path
107
	 * @param INode $node node for which to update properties
108
	 * @param array $changedProperties array of properties to update
109
	 * @return bool
110
	 */
111
	abstract protected function updateProperties($path, INode $node, $changedProperties);
112
113
	/**
114
	 * Bulk load properties for children
115
	 *
116
	 * @param INode $node
117
	 * @param array $requestedProperties requested properties
118
	 *
119
	 * @return void
120
	 */
121
	abstract protected function loadChildrenProperties(INode $node, $requestedProperties);
122
123
	/**
124
	 * This method is called after a node is deleted.
125
	 *
126
	 * @param string $path path of node for which to delete properties
127
	 */
128
	abstract public function delete($path);
129
130
	/**
131
	 * @param string|int $key
132
	 * @return array|null
133
	 */
134
	protected function offsetGet($key){
135
		if (!isset($this->cache[$key])){
136
			return null;
137
		}
138
		return $this->cache[$key];
139
	}
140
141
	/**
142
	 * @param string|int $key
143
	 * @param array $value
144
	 */
145
	protected function offsetSet($key, $value){
146
		$this->cache[$key] = $value;
147
	}
148
149
	/**
150
	 * @param string|int $key
151
	 */
152
	protected function offsetUnset($key){
153
		unset($this->cache[$key]);
154
	}
155
156
	/**
157
	 * Fetches properties for a path.
158
	 *
159
	 * @param string $path
160
	 * @param PropFind $propFind
161
	 * @return void
162
	 */
163
	public function propFind($path, PropFind $propFind) {
164
		$node = $this->getNodeForPath($path);
165
		if (is_null($node)) {
166
			return;
167
		}
168
169
		$requestedProps = $propFind->get404Properties();
170
171
		// these might appear
172
		$requestedProps = array_diff(
173
			$requestedProps,
174
			$this->ignoredProperties
175
		);
176
177
		if (empty($requestedProps)) {
178
			return;
179
		}
180
181
		if ($propFind->getDepth() !== 0) {
182
			$this->loadChildrenProperties($node, $requestedProps);
183
		}
184
185
		$props = $this->getProperties($path, $node, $requestedProps);
186
		foreach ($props as $propName => $propValue) {
187
			$propFind->set($propName, $propValue);
188
		}
189
	}
190
191
	/**
192
	 * Updates properties for a path
193
	 *
194
	 * @param string $path
195
	 * @param PropPatch $propPatch
196
	 *
197
	 * @return void
198
	 */
199
	public function propPatch($path, PropPatch $propPatch) {
200
		$node = $this->getNodeForPath($path);
201
		if (is_null($node)) {
202
			return;
203
		}
204
205
		$propPatch->handleRemaining(function($changedProps) use ($path, $node) {
206
			return $this->updateProperties($path, $node, $changedProps);
207
		});
208
	}
209
210
	/**
211
	 * @param string $sql
212
	 * @param array $whereValues
213
	 * @param array $whereTypes
214
	 * @return array
215
	 */
216
	protected function fetchProperties($sql, $whereValues, $whereTypes) {
217
		$result = $this->connection->executeQuery(
218
			$sql,
219
			$whereValues,
220
			$whereTypes
221
		);
222
223
		$props = [];
224
		while ($row = $result->fetch()) {
225
			$props[$row['propertyname']] = $row['propertyvalue'];
226
		}
227
228
		$result->closeCursor();
229
		return $props;
230
	}
231
232
	/**
233
	 * @param string $path
234
	 * @return INode|null
235
	 */
236
	protected function getNodeForPath($path){
237
		try {
238
			$node = $this->tree->getNodeForPath($path);
239
			return $node;
240
		} catch (ServiceUnavailable $e) {
241
			// might happen for unavailable mount points, skip
242
		} catch (Forbidden $e) {
243
			// might happen for excluded mount points, skip
244
		} catch (NotFound $e) {
245
			// in some rare (buggy) cases the node might not be found,
246
			// we catch the exception to prevent breaking the whole list with a 404
247
			// (soft fail)
248
			\OC::$server->getLogger()->warning(
249
				'Could not get node for path: "{path}" : {$message}',
250
				[
251
					'app' => 'dav',
252
					'path' => $path,
253
					'message' => $e->getMessage(),
254
				]
255
			);
256
		}
257
		return null;
258
	}
259
260
}
261