Completed
Push — stable13 ( f73724...0d8c02 )
by Roeland
11:12
created
apps/dav/lib/DAV/CustomPropertiesBackend.php 1 patch
Indentation   +254 added lines, -254 removed lines patch added patch discarded remove patch
@@ -34,259 +34,259 @@
 block discarded – undo
34 34
 
35 35
 class CustomPropertiesBackend implements BackendInterface {
36 36
 
37
-	/**
38
-	 * Ignored properties
39
-	 *
40
-	 * @var array
41
-	 */
42
-	private $ignoredProperties = array(
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
-		'{http://nextcloud.org/ns}is-encrypted',
53
-	);
54
-
55
-	/**
56
-	 * @var Tree
57
-	 */
58
-	private $tree;
59
-
60
-	/**
61
-	 * @var IDBConnection
62
-	 */
63
-	private $connection;
64
-
65
-	/**
66
-	 * @var string
67
-	 */
68
-	private $user;
69
-
70
-	/**
71
-	 * Properties cache
72
-	 *
73
-	 * @var array
74
-	 */
75
-	private $cache = [];
76
-
77
-	/**
78
-	 * @param Tree $tree node tree
79
-	 * @param IDBConnection $connection database connection
80
-	 * @param IUser $user owner of the tree and properties
81
-	 */
82
-	public function __construct(
83
-		Tree $tree,
84
-		IDBConnection $connection,
85
-		IUser $user) {
86
-		$this->tree = $tree;
87
-		$this->connection = $connection;
88
-		$this->user = $user->getUID();
89
-	}
90
-
91
-	/**
92
-	 * Fetches properties for a path.
93
-	 *
94
-	 * @param string $path
95
-	 * @param PropFind $propFind
96
-	 * @return void
97
-	 */
98
-	public function propFind($path, PropFind $propFind) {
99
-
100
-		$requestedProps = $propFind->get404Properties();
101
-
102
-		// these might appear
103
-		$requestedProps = array_diff(
104
-			$requestedProps,
105
-			$this->ignoredProperties
106
-		);
107
-
108
-		// substr of calendars/ => path is inside the CalDAV component
109
-		// two '/' => this a calendar (no calendar-home nor calendar object)
110
-		if (substr($path, 0, 10) === 'calendars/' && substr_count($path, '/') === 2) {
111
-			$allRequestedProps = $propFind->getRequestedProperties();
112
-			$customPropertiesForShares = [
113
-				'{DAV:}displayname',
114
-				'{urn:ietf:params:xml:ns:caldav}calendar-description',
115
-				'{urn:ietf:params:xml:ns:caldav}calendar-timezone',
116
-				'{http://apple.com/ns/ical/}calendar-order',
117
-				'{http://apple.com/ns/ical/}calendar-color',
118
-				'{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp',
119
-			];
120
-
121
-			foreach ($customPropertiesForShares as $customPropertyForShares) {
122
-				if (in_array($customPropertyForShares, $allRequestedProps)) {
123
-					$requestedProps[] = $customPropertyForShares;
124
-				}
125
-			}
126
-		}
127
-
128
-		if (empty($requestedProps)) {
129
-			return;
130
-		}
131
-
132
-		$props = $this->getProperties($path, $requestedProps);
133
-		foreach ($props as $propName => $propValue) {
134
-			$propFind->set($propName, $propValue);
135
-		}
136
-	}
137
-
138
-	/**
139
-	 * Updates properties for a path
140
-	 *
141
-	 * @param string $path
142
-	 * @param PropPatch $propPatch
143
-	 *
144
-	 * @return void
145
-	 */
146
-	public function propPatch($path, PropPatch $propPatch) {
147
-		$propPatch->handleRemaining(function($changedProps) use ($path) {
148
-			return $this->updateProperties($path, $changedProps);
149
-		});
150
-	}
151
-
152
-	/**
153
-	 * This method is called after a node is deleted.
154
-	 *
155
-	 * @param string $path path of node for which to delete properties
156
-	 */
157
-	public function delete($path) {
158
-		$statement = $this->connection->prepare(
159
-			'DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'
160
-		);
161
-		$statement->execute(array($this->user, $path));
162
-		$statement->closeCursor();
163
-
164
-		unset($this->cache[$path]);
165
-	}
166
-
167
-	/**
168
-	 * This method is called after a successful MOVE
169
-	 *
170
-	 * @param string $source
171
-	 * @param string $destination
172
-	 *
173
-	 * @return void
174
-	 */
175
-	public function move($source, $destination) {
176
-		$statement = $this->connection->prepare(
177
-			'UPDATE `*PREFIX*properties` SET `propertypath` = ?' .
178
-			' WHERE `userid` = ? AND `propertypath` = ?'
179
-		);
180
-		$statement->execute(array($destination, $this->user, $source));
181
-		$statement->closeCursor();
182
-	}
183
-
184
-	/**
185
-	 * Returns a list of properties for this nodes.;
186
-	 * @param string $path
187
-	 * @param array $requestedProperties requested properties or empty array for "all"
188
-	 * @return array
189
-	 * @note The properties list is a list of propertynames the client
190
-	 * requested, encoded as xmlnamespace#tagName, for example:
191
-	 * http://www.example.org/namespace#author If the array is empty, all
192
-	 * properties should be returned
193
-	 */
194
-	private function getProperties($path, array $requestedProperties) {
195
-		if (isset($this->cache[$path])) {
196
-			return $this->cache[$path];
197
-		}
198
-
199
-		// TODO: chunking if more than 1000 properties
200
-		$sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?';
201
-
202
-		$whereValues = array($this->user, $path);
203
-		$whereTypes = array(null, null);
204
-
205
-		if (!empty($requestedProperties)) {
206
-			// request only a subset
207
-			$sql .= ' AND `propertyname` in (?)';
208
-			$whereValues[] = $requestedProperties;
209
-			$whereTypes[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
210
-		}
211
-
212
-		$result = $this->connection->executeQuery(
213
-			$sql,
214
-			$whereValues,
215
-			$whereTypes
216
-		);
217
-
218
-		$props = [];
219
-		while ($row = $result->fetch()) {
220
-			$props[$row['propertyname']] = $row['propertyvalue'];
221
-		}
222
-
223
-		$result->closeCursor();
224
-
225
-		$this->cache[$path] = $props;
226
-		return $props;
227
-	}
228
-
229
-	/**
230
-	 * Update properties
231
-	 *
232
-	 * @param string $path node for which to update properties
233
-	 * @param array $properties array of properties to update
234
-	 *
235
-	 * @return bool
236
-	 */
237
-	private function updateProperties($path, $properties) {
238
-
239
-		$deleteStatement = 'DELETE FROM `*PREFIX*properties`' .
240
-			' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
241
-
242
-		$insertStatement = 'INSERT INTO `*PREFIX*properties`' .
243
-			' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)';
244
-
245
-		$updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' .
246
-			' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
247
-
248
-		// TODO: use "insert or update" strategy ?
249
-		$existing = $this->getProperties($path, array());
250
-		$this->connection->beginTransaction();
251
-		foreach ($properties as $propertyName => $propertyValue) {
252
-			// If it was null, we need to delete the property
253
-			if (is_null($propertyValue)) {
254
-				if (array_key_exists($propertyName, $existing)) {
255
-					$this->connection->executeUpdate($deleteStatement,
256
-						array(
257
-							$this->user,
258
-							$path,
259
-							$propertyName
260
-						)
261
-					);
262
-				}
263
-			} else {
264
-				if (!array_key_exists($propertyName, $existing)) {
265
-					$this->connection->executeUpdate($insertStatement,
266
-						array(
267
-							$this->user,
268
-							$path,
269
-							$propertyName,
270
-							$propertyValue
271
-						)
272
-					);
273
-				} else {
274
-					$this->connection->executeUpdate($updateStatement,
275
-						array(
276
-							$propertyValue,
277
-							$this->user,
278
-							$path,
279
-							$propertyName
280
-						)
281
-					);
282
-				}
283
-			}
284
-		}
285
-
286
-		$this->connection->commit();
287
-		unset($this->cache[$path]);
288
-
289
-		return true;
290
-	}
37
+    /**
38
+     * Ignored properties
39
+     *
40
+     * @var array
41
+     */
42
+    private $ignoredProperties = array(
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
+        '{http://nextcloud.org/ns}is-encrypted',
53
+    );
54
+
55
+    /**
56
+     * @var Tree
57
+     */
58
+    private $tree;
59
+
60
+    /**
61
+     * @var IDBConnection
62
+     */
63
+    private $connection;
64
+
65
+    /**
66
+     * @var string
67
+     */
68
+    private $user;
69
+
70
+    /**
71
+     * Properties cache
72
+     *
73
+     * @var array
74
+     */
75
+    private $cache = [];
76
+
77
+    /**
78
+     * @param Tree $tree node tree
79
+     * @param IDBConnection $connection database connection
80
+     * @param IUser $user owner of the tree and properties
81
+     */
82
+    public function __construct(
83
+        Tree $tree,
84
+        IDBConnection $connection,
85
+        IUser $user) {
86
+        $this->tree = $tree;
87
+        $this->connection = $connection;
88
+        $this->user = $user->getUID();
89
+    }
90
+
91
+    /**
92
+     * Fetches properties for a path.
93
+     *
94
+     * @param string $path
95
+     * @param PropFind $propFind
96
+     * @return void
97
+     */
98
+    public function propFind($path, PropFind $propFind) {
99
+
100
+        $requestedProps = $propFind->get404Properties();
101
+
102
+        // these might appear
103
+        $requestedProps = array_diff(
104
+            $requestedProps,
105
+            $this->ignoredProperties
106
+        );
107
+
108
+        // substr of calendars/ => path is inside the CalDAV component
109
+        // two '/' => this a calendar (no calendar-home nor calendar object)
110
+        if (substr($path, 0, 10) === 'calendars/' && substr_count($path, '/') === 2) {
111
+            $allRequestedProps = $propFind->getRequestedProperties();
112
+            $customPropertiesForShares = [
113
+                '{DAV:}displayname',
114
+                '{urn:ietf:params:xml:ns:caldav}calendar-description',
115
+                '{urn:ietf:params:xml:ns:caldav}calendar-timezone',
116
+                '{http://apple.com/ns/ical/}calendar-order',
117
+                '{http://apple.com/ns/ical/}calendar-color',
118
+                '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp',
119
+            ];
120
+
121
+            foreach ($customPropertiesForShares as $customPropertyForShares) {
122
+                if (in_array($customPropertyForShares, $allRequestedProps)) {
123
+                    $requestedProps[] = $customPropertyForShares;
124
+                }
125
+            }
126
+        }
127
+
128
+        if (empty($requestedProps)) {
129
+            return;
130
+        }
131
+
132
+        $props = $this->getProperties($path, $requestedProps);
133
+        foreach ($props as $propName => $propValue) {
134
+            $propFind->set($propName, $propValue);
135
+        }
136
+    }
137
+
138
+    /**
139
+     * Updates properties for a path
140
+     *
141
+     * @param string $path
142
+     * @param PropPatch $propPatch
143
+     *
144
+     * @return void
145
+     */
146
+    public function propPatch($path, PropPatch $propPatch) {
147
+        $propPatch->handleRemaining(function($changedProps) use ($path) {
148
+            return $this->updateProperties($path, $changedProps);
149
+        });
150
+    }
151
+
152
+    /**
153
+     * This method is called after a node is deleted.
154
+     *
155
+     * @param string $path path of node for which to delete properties
156
+     */
157
+    public function delete($path) {
158
+        $statement = $this->connection->prepare(
159
+            'DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'
160
+        );
161
+        $statement->execute(array($this->user, $path));
162
+        $statement->closeCursor();
163
+
164
+        unset($this->cache[$path]);
165
+    }
166
+
167
+    /**
168
+     * This method is called after a successful MOVE
169
+     *
170
+     * @param string $source
171
+     * @param string $destination
172
+     *
173
+     * @return void
174
+     */
175
+    public function move($source, $destination) {
176
+        $statement = $this->connection->prepare(
177
+            'UPDATE `*PREFIX*properties` SET `propertypath` = ?' .
178
+            ' WHERE `userid` = ? AND `propertypath` = ?'
179
+        );
180
+        $statement->execute(array($destination, $this->user, $source));
181
+        $statement->closeCursor();
182
+    }
183
+
184
+    /**
185
+     * Returns a list of properties for this nodes.;
186
+     * @param string $path
187
+     * @param array $requestedProperties requested properties or empty array for "all"
188
+     * @return array
189
+     * @note The properties list is a list of propertynames the client
190
+     * requested, encoded as xmlnamespace#tagName, for example:
191
+     * http://www.example.org/namespace#author If the array is empty, all
192
+     * properties should be returned
193
+     */
194
+    private function getProperties($path, array $requestedProperties) {
195
+        if (isset($this->cache[$path])) {
196
+            return $this->cache[$path];
197
+        }
198
+
199
+        // TODO: chunking if more than 1000 properties
200
+        $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?';
201
+
202
+        $whereValues = array($this->user, $path);
203
+        $whereTypes = array(null, null);
204
+
205
+        if (!empty($requestedProperties)) {
206
+            // request only a subset
207
+            $sql .= ' AND `propertyname` in (?)';
208
+            $whereValues[] = $requestedProperties;
209
+            $whereTypes[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
210
+        }
211
+
212
+        $result = $this->connection->executeQuery(
213
+            $sql,
214
+            $whereValues,
215
+            $whereTypes
216
+        );
217
+
218
+        $props = [];
219
+        while ($row = $result->fetch()) {
220
+            $props[$row['propertyname']] = $row['propertyvalue'];
221
+        }
222
+
223
+        $result->closeCursor();
224
+
225
+        $this->cache[$path] = $props;
226
+        return $props;
227
+    }
228
+
229
+    /**
230
+     * Update properties
231
+     *
232
+     * @param string $path node for which to update properties
233
+     * @param array $properties array of properties to update
234
+     *
235
+     * @return bool
236
+     */
237
+    private function updateProperties($path, $properties) {
238
+
239
+        $deleteStatement = 'DELETE FROM `*PREFIX*properties`' .
240
+            ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
241
+
242
+        $insertStatement = 'INSERT INTO `*PREFIX*properties`' .
243
+            ' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)';
244
+
245
+        $updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' .
246
+            ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
247
+
248
+        // TODO: use "insert or update" strategy ?
249
+        $existing = $this->getProperties($path, array());
250
+        $this->connection->beginTransaction();
251
+        foreach ($properties as $propertyName => $propertyValue) {
252
+            // If it was null, we need to delete the property
253
+            if (is_null($propertyValue)) {
254
+                if (array_key_exists($propertyName, $existing)) {
255
+                    $this->connection->executeUpdate($deleteStatement,
256
+                        array(
257
+                            $this->user,
258
+                            $path,
259
+                            $propertyName
260
+                        )
261
+                    );
262
+                }
263
+            } else {
264
+                if (!array_key_exists($propertyName, $existing)) {
265
+                    $this->connection->executeUpdate($insertStatement,
266
+                        array(
267
+                            $this->user,
268
+                            $path,
269
+                            $propertyName,
270
+                            $propertyValue
271
+                        )
272
+                    );
273
+                } else {
274
+                    $this->connection->executeUpdate($updateStatement,
275
+                        array(
276
+                            $propertyValue,
277
+                            $this->user,
278
+                            $path,
279
+                            $propertyName
280
+                        )
281
+                    );
282
+                }
283
+            }
284
+        }
285
+
286
+        $this->connection->commit();
287
+        unset($this->cache[$path]);
288
+
289
+        return true;
290
+    }
291 291
 
292 292
 }
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/CustomPropertiesBackend.php 1 patch
Indentation   +317 added lines, -317 removed lines patch added patch discarded remove patch
@@ -36,322 +36,322 @@
 block discarded – undo
36 36
 
37 37
 class CustomPropertiesBackend implements BackendInterface {
38 38
 
39
-	/**
40
-	 * Ignored properties
41
-	 *
42
-	 * @var array
43
-	 */
44
-	private $ignoredProperties = array(
45
-		'{DAV:}getcontentlength',
46
-		'{DAV:}getcontenttype',
47
-		'{DAV:}getetag',
48
-		'{DAV:}quota-used-bytes',
49
-		'{DAV:}quota-available-bytes',
50
-		'{http://owncloud.org/ns}permissions',
51
-		'{http://owncloud.org/ns}downloadURL',
52
-		'{http://owncloud.org/ns}dDC',
53
-		'{http://owncloud.org/ns}size',
54
-		'{http://nextcloud.org/ns}is-encrypted',
55
-	);
56
-
57
-	/**
58
-	 * @var Tree
59
-	 */
60
-	private $tree;
61
-
62
-	/**
63
-	 * @var IDBConnection
64
-	 */
65
-	private $connection;
66
-
67
-	/**
68
-	 * @var IUser
69
-	 */
70
-	private $user;
71
-
72
-	/**
73
-	 * Properties cache
74
-	 *
75
-	 * @var array
76
-	 */
77
-	private $cache = [];
78
-
79
-	/**
80
-	 * @param Tree $tree node tree
81
-	 * @param IDBConnection $connection database connection
82
-	 * @param IUser $user owner of the tree and properties
83
-	 */
84
-	public function __construct(
85
-		Tree $tree,
86
-		IDBConnection $connection,
87
-		IUser $user) {
88
-		$this->tree = $tree;
89
-		$this->connection = $connection;
90
-		$this->user = $user->getUID();
91
-	}
92
-
93
-	/**
94
-	 * Fetches properties for a path.
95
-	 *
96
-	 * @param string $path
97
-	 * @param PropFind $propFind
98
-	 * @return void
99
-	 */
100
-	public function propFind($path, PropFind $propFind) {
101
-		try {
102
-			$node = $this->tree->getNodeForPath($path);
103
-			if (!($node instanceof Node)) {
104
-				return;
105
-			}
106
-		} catch (ServiceUnavailable $e) {
107
-			// might happen for unavailable mount points, skip
108
-			return;
109
-		} catch (NotFound $e) {
110
-			// in some rare (buggy) cases the node might not be found,
111
-			// we catch the exception to prevent breaking the whole list with a 404
112
-			// (soft fail)
113
-			\OC::$server->getLogger()->warning(
114
-				'Could not get node for path: \"' . $path . '\" : ' . $e->getMessage(),
115
-				array('app' => 'files')
116
-			);
117
-			return;
118
-		}
119
-
120
-		$requestedProps = $propFind->get404Properties();
121
-
122
-		// these might appear
123
-		$requestedProps = array_diff(
124
-			$requestedProps,
125
-			$this->ignoredProperties
126
-		);
127
-
128
-		if (empty($requestedProps)) {
129
-			return;
130
-		}
131
-
132
-		if ($node instanceof Directory
133
-			&& $propFind->getDepth() !== 0
134
-		) {
135
-			// note: pre-fetching only supported for depth <= 1
136
-			$this->loadChildrenProperties($node, $requestedProps);
137
-		}
138
-
139
-		$props = $this->getProperties($node, $requestedProps);
140
-		foreach ($props as $propName => $propValue) {
141
-			$propFind->set($propName, $propValue);
142
-		}
143
-	}
144
-
145
-	/**
146
-	 * Updates properties for a path
147
-	 *
148
-	 * @param string $path
149
-	 * @param PropPatch $propPatch
150
-	 *
151
-	 * @return void
152
-	 */
153
-	public function propPatch($path, PropPatch $propPatch) {
154
-		$node = $this->tree->getNodeForPath($path);
155
-		if (!($node instanceof Node)) {
156
-			return;
157
-		}
158
-
159
-		$propPatch->handleRemaining(function($changedProps) use ($node) {
160
-			return $this->updateProperties($node, $changedProps);
161
-		});
162
-	}
163
-
164
-	/**
165
-	 * This method is called after a node is deleted.
166
-	 *
167
-	 * @param string $path path of node for which to delete properties
168
-	 */
169
-	public function delete($path) {
170
-		$statement = $this->connection->prepare(
171
-			'DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'
172
-		);
173
-		$statement->execute(array($this->user, '/' . $path));
174
-		$statement->closeCursor();
175
-
176
-		unset($this->cache[$path]);
177
-	}
178
-
179
-	/**
180
-	 * This method is called after a successful MOVE
181
-	 *
182
-	 * @param string $source
183
-	 * @param string $destination
184
-	 *
185
-	 * @return void
186
-	 */
187
-	public function move($source, $destination) {
188
-		$statement = $this->connection->prepare(
189
-			'UPDATE `*PREFIX*properties` SET `propertypath` = ?' .
190
-			' WHERE `userid` = ? AND `propertypath` = ?'
191
-		);
192
-		$statement->execute(array('/' . $destination, $this->user, '/' . $source));
193
-		$statement->closeCursor();
194
-	}
195
-
196
-	/**
197
-	 * Returns a list of properties for this nodes.;
198
-	 * @param Node $node
199
-	 * @param array $requestedProperties requested properties or empty array for "all"
200
-	 * @return array
201
-	 * @note The properties list is a list of propertynames the client
202
-	 * requested, encoded as xmlnamespace#tagName, for example:
203
-	 * http://www.example.org/namespace#author If the array is empty, all
204
-	 * properties should be returned
205
-	 */
206
-	private function getProperties(Node $node, array $requestedProperties) {
207
-		$path = $node->getPath();
208
-		if (isset($this->cache[$path])) {
209
-			return $this->cache[$path];
210
-		}
211
-
212
-		// TODO: chunking if more than 1000 properties
213
-		$sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?';
214
-
215
-		$whereValues = array($this->user, $path);
216
-		$whereTypes = array(null, null);
217
-
218
-		if (!empty($requestedProperties)) {
219
-			// request only a subset
220
-			$sql .= ' AND `propertyname` in (?)';
221
-			$whereValues[] = $requestedProperties;
222
-			$whereTypes[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
223
-		}
224
-
225
-		$result = $this->connection->executeQuery(
226
-			$sql,
227
-			$whereValues,
228
-			$whereTypes
229
-		);
230
-
231
-		$props = [];
232
-		while ($row = $result->fetch()) {
233
-			$props[$row['propertyname']] = $row['propertyvalue'];
234
-		}
235
-
236
-		$result->closeCursor();
237
-
238
-		$this->cache[$path] = $props;
239
-		return $props;
240
-	}
241
-
242
-	/**
243
-	 * Update properties
244
-	 *
245
-	 * @param Node $node node for which to update properties
246
-	 * @param array $properties array of properties to update
247
-	 *
248
-	 * @return bool
249
-	 */
250
-	private function updateProperties($node, $properties) {
251
-		$path = $node->getPath();
252
-
253
-		$deleteStatement = 'DELETE FROM `*PREFIX*properties`' .
254
-			' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
255
-
256
-		$insertStatement = 'INSERT INTO `*PREFIX*properties`' .
257
-			' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)';
258
-
259
-		$updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' .
260
-			' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
261
-
262
-		// TODO: use "insert or update" strategy ?
263
-		$existing = $this->getProperties($node, array());
264
-		$this->connection->beginTransaction();
265
-		foreach ($properties as $propertyName => $propertyValue) {
266
-			// If it was null, we need to delete the property
267
-			if (is_null($propertyValue)) {
268
-				if (array_key_exists($propertyName, $existing)) {
269
-					$this->connection->executeUpdate($deleteStatement,
270
-						array(
271
-							$this->user,
272
-							$path,
273
-							$propertyName
274
-						)
275
-					);
276
-				}
277
-			} else {
278
-				if (!array_key_exists($propertyName, $existing)) {
279
-					$this->connection->executeUpdate($insertStatement,
280
-						array(
281
-							$this->user,
282
-							$path,
283
-							$propertyName,
284
-							$propertyValue
285
-						)
286
-					);
287
-				} else {
288
-					$this->connection->executeUpdate($updateStatement,
289
-						array(
290
-							$propertyValue,
291
-							$this->user,
292
-							$path,
293
-							$propertyName
294
-						)
295
-					);
296
-				}
297
-			}
298
-		}
299
-
300
-		$this->connection->commit();
301
-		unset($this->cache[$path]);
302
-
303
-		return true;
304
-	}
305
-
306
-	/**
307
-	 * Bulk load properties for directory children
308
-	 *
309
-	 * @param Directory $node
310
-	 * @param array $requestedProperties requested properties
311
-	 *
312
-	 * @return void
313
-	 */
314
-	private function loadChildrenProperties(Directory $node, $requestedProperties) {
315
-		$path = $node->getPath();
316
-		if (isset($this->cache[$path])) {
317
-			// we already loaded them at some point
318
-			return;
319
-		}
320
-
321
-		$childNodes = $node->getChildren();
322
-		// pre-fill cache
323
-		foreach ($childNodes as $childNode) {
324
-			$this->cache[$childNode->getPath()] = [];
325
-		}
326
-
327
-		$sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` LIKE ?';
328
-		$sql .= ' AND `propertyname` in (?) ORDER BY `propertypath`, `propertyname`';
329
-
330
-		$result = $this->connection->executeQuery(
331
-			$sql,
332
-			array($this->user, $this->connection->escapeLikeParameter(rtrim($path, '/')) . '/%', $requestedProperties),
333
-			array(null, null, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
334
-		);
335
-
336
-		$oldPath = null;
337
-		$props = [];
338
-		while ($row = $result->fetch()) {
339
-			$path = $row['propertypath'];
340
-			if ($oldPath !== $path) {
341
-				// save previously gathered props
342
-				$this->cache[$oldPath] = $props;
343
-				$oldPath = $path;
344
-				// prepare props for next path
345
-				$props = [];
346
-			}
347
-			$props[$row['propertyname']] = $row['propertyvalue'];
348
-		}
349
-		if (!is_null($oldPath)) {
350
-			// save props from last run
351
-			$this->cache[$oldPath] = $props;
352
-		}
353
-
354
-		$result->closeCursor();
355
-	}
39
+    /**
40
+     * Ignored properties
41
+     *
42
+     * @var array
43
+     */
44
+    private $ignoredProperties = array(
45
+        '{DAV:}getcontentlength',
46
+        '{DAV:}getcontenttype',
47
+        '{DAV:}getetag',
48
+        '{DAV:}quota-used-bytes',
49
+        '{DAV:}quota-available-bytes',
50
+        '{http://owncloud.org/ns}permissions',
51
+        '{http://owncloud.org/ns}downloadURL',
52
+        '{http://owncloud.org/ns}dDC',
53
+        '{http://owncloud.org/ns}size',
54
+        '{http://nextcloud.org/ns}is-encrypted',
55
+    );
56
+
57
+    /**
58
+     * @var Tree
59
+     */
60
+    private $tree;
61
+
62
+    /**
63
+     * @var IDBConnection
64
+     */
65
+    private $connection;
66
+
67
+    /**
68
+     * @var IUser
69
+     */
70
+    private $user;
71
+
72
+    /**
73
+     * Properties cache
74
+     *
75
+     * @var array
76
+     */
77
+    private $cache = [];
78
+
79
+    /**
80
+     * @param Tree $tree node tree
81
+     * @param IDBConnection $connection database connection
82
+     * @param IUser $user owner of the tree and properties
83
+     */
84
+    public function __construct(
85
+        Tree $tree,
86
+        IDBConnection $connection,
87
+        IUser $user) {
88
+        $this->tree = $tree;
89
+        $this->connection = $connection;
90
+        $this->user = $user->getUID();
91
+    }
92
+
93
+    /**
94
+     * Fetches properties for a path.
95
+     *
96
+     * @param string $path
97
+     * @param PropFind $propFind
98
+     * @return void
99
+     */
100
+    public function propFind($path, PropFind $propFind) {
101
+        try {
102
+            $node = $this->tree->getNodeForPath($path);
103
+            if (!($node instanceof Node)) {
104
+                return;
105
+            }
106
+        } catch (ServiceUnavailable $e) {
107
+            // might happen for unavailable mount points, skip
108
+            return;
109
+        } catch (NotFound $e) {
110
+            // in some rare (buggy) cases the node might not be found,
111
+            // we catch the exception to prevent breaking the whole list with a 404
112
+            // (soft fail)
113
+            \OC::$server->getLogger()->warning(
114
+                'Could not get node for path: \"' . $path . '\" : ' . $e->getMessage(),
115
+                array('app' => 'files')
116
+            );
117
+            return;
118
+        }
119
+
120
+        $requestedProps = $propFind->get404Properties();
121
+
122
+        // these might appear
123
+        $requestedProps = array_diff(
124
+            $requestedProps,
125
+            $this->ignoredProperties
126
+        );
127
+
128
+        if (empty($requestedProps)) {
129
+            return;
130
+        }
131
+
132
+        if ($node instanceof Directory
133
+            && $propFind->getDepth() !== 0
134
+        ) {
135
+            // note: pre-fetching only supported for depth <= 1
136
+            $this->loadChildrenProperties($node, $requestedProps);
137
+        }
138
+
139
+        $props = $this->getProperties($node, $requestedProps);
140
+        foreach ($props as $propName => $propValue) {
141
+            $propFind->set($propName, $propValue);
142
+        }
143
+    }
144
+
145
+    /**
146
+     * Updates properties for a path
147
+     *
148
+     * @param string $path
149
+     * @param PropPatch $propPatch
150
+     *
151
+     * @return void
152
+     */
153
+    public function propPatch($path, PropPatch $propPatch) {
154
+        $node = $this->tree->getNodeForPath($path);
155
+        if (!($node instanceof Node)) {
156
+            return;
157
+        }
158
+
159
+        $propPatch->handleRemaining(function($changedProps) use ($node) {
160
+            return $this->updateProperties($node, $changedProps);
161
+        });
162
+    }
163
+
164
+    /**
165
+     * This method is called after a node is deleted.
166
+     *
167
+     * @param string $path path of node for which to delete properties
168
+     */
169
+    public function delete($path) {
170
+        $statement = $this->connection->prepare(
171
+            'DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'
172
+        );
173
+        $statement->execute(array($this->user, '/' . $path));
174
+        $statement->closeCursor();
175
+
176
+        unset($this->cache[$path]);
177
+    }
178
+
179
+    /**
180
+     * This method is called after a successful MOVE
181
+     *
182
+     * @param string $source
183
+     * @param string $destination
184
+     *
185
+     * @return void
186
+     */
187
+    public function move($source, $destination) {
188
+        $statement = $this->connection->prepare(
189
+            'UPDATE `*PREFIX*properties` SET `propertypath` = ?' .
190
+            ' WHERE `userid` = ? AND `propertypath` = ?'
191
+        );
192
+        $statement->execute(array('/' . $destination, $this->user, '/' . $source));
193
+        $statement->closeCursor();
194
+    }
195
+
196
+    /**
197
+     * Returns a list of properties for this nodes.;
198
+     * @param Node $node
199
+     * @param array $requestedProperties requested properties or empty array for "all"
200
+     * @return array
201
+     * @note The properties list is a list of propertynames the client
202
+     * requested, encoded as xmlnamespace#tagName, for example:
203
+     * http://www.example.org/namespace#author If the array is empty, all
204
+     * properties should be returned
205
+     */
206
+    private function getProperties(Node $node, array $requestedProperties) {
207
+        $path = $node->getPath();
208
+        if (isset($this->cache[$path])) {
209
+            return $this->cache[$path];
210
+        }
211
+
212
+        // TODO: chunking if more than 1000 properties
213
+        $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?';
214
+
215
+        $whereValues = array($this->user, $path);
216
+        $whereTypes = array(null, null);
217
+
218
+        if (!empty($requestedProperties)) {
219
+            // request only a subset
220
+            $sql .= ' AND `propertyname` in (?)';
221
+            $whereValues[] = $requestedProperties;
222
+            $whereTypes[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
223
+        }
224
+
225
+        $result = $this->connection->executeQuery(
226
+            $sql,
227
+            $whereValues,
228
+            $whereTypes
229
+        );
230
+
231
+        $props = [];
232
+        while ($row = $result->fetch()) {
233
+            $props[$row['propertyname']] = $row['propertyvalue'];
234
+        }
235
+
236
+        $result->closeCursor();
237
+
238
+        $this->cache[$path] = $props;
239
+        return $props;
240
+    }
241
+
242
+    /**
243
+     * Update properties
244
+     *
245
+     * @param Node $node node for which to update properties
246
+     * @param array $properties array of properties to update
247
+     *
248
+     * @return bool
249
+     */
250
+    private function updateProperties($node, $properties) {
251
+        $path = $node->getPath();
252
+
253
+        $deleteStatement = 'DELETE FROM `*PREFIX*properties`' .
254
+            ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
255
+
256
+        $insertStatement = 'INSERT INTO `*PREFIX*properties`' .
257
+            ' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)';
258
+
259
+        $updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' .
260
+            ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
261
+
262
+        // TODO: use "insert or update" strategy ?
263
+        $existing = $this->getProperties($node, array());
264
+        $this->connection->beginTransaction();
265
+        foreach ($properties as $propertyName => $propertyValue) {
266
+            // If it was null, we need to delete the property
267
+            if (is_null($propertyValue)) {
268
+                if (array_key_exists($propertyName, $existing)) {
269
+                    $this->connection->executeUpdate($deleteStatement,
270
+                        array(
271
+                            $this->user,
272
+                            $path,
273
+                            $propertyName
274
+                        )
275
+                    );
276
+                }
277
+            } else {
278
+                if (!array_key_exists($propertyName, $existing)) {
279
+                    $this->connection->executeUpdate($insertStatement,
280
+                        array(
281
+                            $this->user,
282
+                            $path,
283
+                            $propertyName,
284
+                            $propertyValue
285
+                        )
286
+                    );
287
+                } else {
288
+                    $this->connection->executeUpdate($updateStatement,
289
+                        array(
290
+                            $propertyValue,
291
+                            $this->user,
292
+                            $path,
293
+                            $propertyName
294
+                        )
295
+                    );
296
+                }
297
+            }
298
+        }
299
+
300
+        $this->connection->commit();
301
+        unset($this->cache[$path]);
302
+
303
+        return true;
304
+    }
305
+
306
+    /**
307
+     * Bulk load properties for directory children
308
+     *
309
+     * @param Directory $node
310
+     * @param array $requestedProperties requested properties
311
+     *
312
+     * @return void
313
+     */
314
+    private function loadChildrenProperties(Directory $node, $requestedProperties) {
315
+        $path = $node->getPath();
316
+        if (isset($this->cache[$path])) {
317
+            // we already loaded them at some point
318
+            return;
319
+        }
320
+
321
+        $childNodes = $node->getChildren();
322
+        // pre-fill cache
323
+        foreach ($childNodes as $childNode) {
324
+            $this->cache[$childNode->getPath()] = [];
325
+        }
326
+
327
+        $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` LIKE ?';
328
+        $sql .= ' AND `propertyname` in (?) ORDER BY `propertypath`, `propertyname`';
329
+
330
+        $result = $this->connection->executeQuery(
331
+            $sql,
332
+            array($this->user, $this->connection->escapeLikeParameter(rtrim($path, '/')) . '/%', $requestedProperties),
333
+            array(null, null, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
334
+        );
335
+
336
+        $oldPath = null;
337
+        $props = [];
338
+        while ($row = $result->fetch()) {
339
+            $path = $row['propertypath'];
340
+            if ($oldPath !== $path) {
341
+                // save previously gathered props
342
+                $this->cache[$oldPath] = $props;
343
+                $oldPath = $path;
344
+                // prepare props for next path
345
+                $props = [];
346
+            }
347
+            $props[$row['propertyname']] = $row['propertyvalue'];
348
+        }
349
+        if (!is_null($oldPath)) {
350
+            // save props from last run
351
+            $this->cache[$oldPath] = $props;
352
+        }
353
+
354
+        $result->closeCursor();
355
+    }
356 356
 
357 357
 }
Please login to merge, or discard this patch.