Completed
Push — master ( 01feb8...89fcef )
by
unknown
30:48
created
lib/public/Files/Folder.php 1 patch
Indentation   +203 added lines, -203 removed lines patch added patch discarded remove patch
@@ -16,207 +16,207 @@
 block discarded – undo
16 16
  * @since 6.0.0
17 17
  */
18 18
 interface Folder extends Node {
19
-	/**
20
-	 * Get the full path of an item in the folder within owncloud's filesystem
21
-	 *
22
-	 * @param string $path relative path of an item in the folder
23
-	 * @return string
24
-	 * @throws \OCP\Files\NotPermittedException
25
-	 * @since 6.0.0
26
-	 */
27
-	public function getFullPath($path);
28
-
29
-	/**
30
-	 * Get the path of an item in the folder relative to the folder
31
-	 *
32
-	 * @param string $path absolute path of an item in the folder
33
-	 * @throws \OCP\Files\NotFoundException
34
-	 * @return string|null
35
-	 * @since 6.0.0
36
-	 */
37
-	public function getRelativePath($path);
38
-
39
-	/**
40
-	 * check if a node is a (grand-)child of the folder
41
-	 *
42
-	 * @param \OCP\Files\Node $node
43
-	 * @return bool
44
-	 * @since 6.0.0
45
-	 */
46
-	public function isSubNode($node);
47
-
48
-	/**
49
-	 * get the content of this directory
50
-	 *
51
-	 * @throws \OCP\Files\NotFoundException
52
-	 * @return \OCP\Files\Node[]
53
-	 * @since 6.0.0
54
-	 */
55
-	public function getDirectoryListing();
56
-
57
-	/**
58
-	 * Get the node at $path
59
-	 *
60
-	 * @param string $path relative path of the file or folder
61
-	 * @return \OCP\Files\Node
62
-	 * @throws \OCP\Files\NotFoundException
63
-	 * @throws \OCP\Files\NotPermittedException
64
-	 * @since 6.0.0
65
-	 */
66
-	public function get($path);
67
-
68
-	/**
69
-	 * Get or create new folder if the folder does not already exist.
70
-	 *
71
-	 * @param string $path relative path of the file or folder
72
-	 * @throw \OCP\Files\NotPermittedException
73
-	 * @since 33.0.0
74
-	 */
75
-	public function getOrCreateFolder(string $path, int $maxRetries = 5): Folder;
76
-
77
-	/**
78
-	 * Check if a file or folder exists in the folder
79
-	 *
80
-	 * @param string $path relative path of the file or folder
81
-	 * @return bool
82
-	 * @since 6.0.0
83
-	 */
84
-	public function nodeExists($path);
85
-
86
-	/**
87
-	 * Create a new folder
88
-	 *
89
-	 * @param string $path relative path of the new folder
90
-	 * @return \OCP\Files\Folder
91
-	 * @throws \OCP\Files\NotPermittedException
92
-	 * @since 6.0.0
93
-	 */
94
-	public function newFolder($path);
95
-
96
-	/**
97
-	 * Create a new file
98
-	 *
99
-	 * @param string $path relative path of the new file
100
-	 * @param string|resource|null $content content for the new file, since 19.0.0
101
-	 * @return \OCP\Files\File
102
-	 * @throws \OCP\Files\NotPermittedException
103
-	 * @since 6.0.0
104
-	 */
105
-	public function newFile($path, $content = null);
106
-
107
-	/**
108
-	 * search for files with the name matching $query
109
-	 *
110
-	 * @param string|ISearchQuery $query
111
-	 * @return \OCP\Files\Node[]
112
-	 * @since 6.0.0
113
-	 */
114
-	public function search($query);
115
-
116
-	/**
117
-	 * search for files by mimetype
118
-	 * $mimetype can either be a full mimetype (image/png) or a wildcard mimetype (image)
119
-	 *
120
-	 * @param string $mimetype
121
-	 * @return \OCP\Files\Node[]
122
-	 * @since 6.0.0
123
-	 */
124
-	public function searchByMime($mimetype);
125
-
126
-	/**
127
-	 * search for files by tag
128
-	 *
129
-	 * @param string|int $tag tag name or tag id
130
-	 * @param string $userId owner of the tags
131
-	 * @return \OCP\Files\Node[]
132
-	 * @since 8.0.0
133
-	 */
134
-	public function searchByTag($tag, $userId);
135
-
136
-	/**
137
-	 * search for files by system tag
138
-	 *
139
-	 * @param string|int $tag tag name
140
-	 * @param string $userId user id to ensure access on returned nodes
141
-	 * @return \OCP\Files\Node[]
142
-	 * @since 28.0.0
143
-	 */
144
-	public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0);
145
-
146
-	/**
147
-	 * get a file or folder inside the folder by its internal id
148
-	 *
149
-	 * This method could return multiple entries. For example once the file/folder
150
-	 * is shared or mounted (files_external) to the user multiple times.
151
-	 *
152
-	 * Note that the different entries can have different permissions.
153
-	 *
154
-	 * @param int $id
155
-	 * @return \OCP\Files\Node[]
156
-	 * @since 6.0.0
157
-	 */
158
-	public function getById($id);
159
-
160
-	/**
161
-	 * get a file or folder inside the folder by its internal id
162
-	 *
163
-	 * Unlike getById, this method only returns a single node even if the user has
164
-	 * access to the file with the requested id multiple times.
165
-	 *
166
-	 * This method provides no guarantee about which of the nodes in returned and the
167
-	 * returned node might, for example, have less permissions than other nodes for the same file
168
-	 *
169
-	 * Apps that require accurate information about the users access to the file should use getById
170
-	 * instead of pick the correct node out of the result.
171
-	 *
172
-	 * @param int $id
173
-	 * @return Node|null
174
-	 * @since 29.0.0
175
-	 */
176
-	public function getFirstNodeById(int $id): ?Node;
177
-
178
-	/**
179
-	 * Get the amount of free space inside the folder
180
-	 *
181
-	 * @return int
182
-	 * @since 6.0.0
183
-	 */
184
-	public function getFreeSpace();
185
-
186
-	/**
187
-	 * Check if new files or folders can be created within the folder
188
-	 *
189
-	 * @return bool
190
-	 * @since 6.0.0
191
-	 */
192
-	public function isCreatable();
193
-
194
-	/**
195
-	 * Add a suffix to the name in case the file exists
196
-	 *
197
-	 * @param string $filename
198
-	 * @return string
199
-	 * @throws NotPermittedException
200
-	 * @since 8.1.0
201
-	 */
202
-	public function getNonExistingName($filename);
203
-
204
-	/**
205
-	 * @param int $limit
206
-	 * @param int $offset
207
-	 * @return \OCP\Files\Node[]
208
-	 * @since 9.1.0
209
-	 */
210
-	public function getRecent($limit, $offset = 0);
211
-
212
-	/**
213
-	 * Verify if the given path is valid and allowed from this folder.
214
-	 *
215
-	 * @param string $path the path from this folder
216
-	 * @param string $fileName
217
-	 * @param bool $readonly Check only if the path is allowed for read-only access
218
-	 * @throws InvalidPathException
219
-	 * @since 32.0.0
220
-	 */
221
-	public function verifyPath($fileName, $readonly = false): void;
19
+    /**
20
+     * Get the full path of an item in the folder within owncloud's filesystem
21
+     *
22
+     * @param string $path relative path of an item in the folder
23
+     * @return string
24
+     * @throws \OCP\Files\NotPermittedException
25
+     * @since 6.0.0
26
+     */
27
+    public function getFullPath($path);
28
+
29
+    /**
30
+     * Get the path of an item in the folder relative to the folder
31
+     *
32
+     * @param string $path absolute path of an item in the folder
33
+     * @throws \OCP\Files\NotFoundException
34
+     * @return string|null
35
+     * @since 6.0.0
36
+     */
37
+    public function getRelativePath($path);
38
+
39
+    /**
40
+     * check if a node is a (grand-)child of the folder
41
+     *
42
+     * @param \OCP\Files\Node $node
43
+     * @return bool
44
+     * @since 6.0.0
45
+     */
46
+    public function isSubNode($node);
47
+
48
+    /**
49
+     * get the content of this directory
50
+     *
51
+     * @throws \OCP\Files\NotFoundException
52
+     * @return \OCP\Files\Node[]
53
+     * @since 6.0.0
54
+     */
55
+    public function getDirectoryListing();
56
+
57
+    /**
58
+     * Get the node at $path
59
+     *
60
+     * @param string $path relative path of the file or folder
61
+     * @return \OCP\Files\Node
62
+     * @throws \OCP\Files\NotFoundException
63
+     * @throws \OCP\Files\NotPermittedException
64
+     * @since 6.0.0
65
+     */
66
+    public function get($path);
67
+
68
+    /**
69
+     * Get or create new folder if the folder does not already exist.
70
+     *
71
+     * @param string $path relative path of the file or folder
72
+     * @throw \OCP\Files\NotPermittedException
73
+     * @since 33.0.0
74
+     */
75
+    public function getOrCreateFolder(string $path, int $maxRetries = 5): Folder;
76
+
77
+    /**
78
+     * Check if a file or folder exists in the folder
79
+     *
80
+     * @param string $path relative path of the file or folder
81
+     * @return bool
82
+     * @since 6.0.0
83
+     */
84
+    public function nodeExists($path);
85
+
86
+    /**
87
+     * Create a new folder
88
+     *
89
+     * @param string $path relative path of the new folder
90
+     * @return \OCP\Files\Folder
91
+     * @throws \OCP\Files\NotPermittedException
92
+     * @since 6.0.0
93
+     */
94
+    public function newFolder($path);
95
+
96
+    /**
97
+     * Create a new file
98
+     *
99
+     * @param string $path relative path of the new file
100
+     * @param string|resource|null $content content for the new file, since 19.0.0
101
+     * @return \OCP\Files\File
102
+     * @throws \OCP\Files\NotPermittedException
103
+     * @since 6.0.0
104
+     */
105
+    public function newFile($path, $content = null);
106
+
107
+    /**
108
+     * search for files with the name matching $query
109
+     *
110
+     * @param string|ISearchQuery $query
111
+     * @return \OCP\Files\Node[]
112
+     * @since 6.0.0
113
+     */
114
+    public function search($query);
115
+
116
+    /**
117
+     * search for files by mimetype
118
+     * $mimetype can either be a full mimetype (image/png) or a wildcard mimetype (image)
119
+     *
120
+     * @param string $mimetype
121
+     * @return \OCP\Files\Node[]
122
+     * @since 6.0.0
123
+     */
124
+    public function searchByMime($mimetype);
125
+
126
+    /**
127
+     * search for files by tag
128
+     *
129
+     * @param string|int $tag tag name or tag id
130
+     * @param string $userId owner of the tags
131
+     * @return \OCP\Files\Node[]
132
+     * @since 8.0.0
133
+     */
134
+    public function searchByTag($tag, $userId);
135
+
136
+    /**
137
+     * search for files by system tag
138
+     *
139
+     * @param string|int $tag tag name
140
+     * @param string $userId user id to ensure access on returned nodes
141
+     * @return \OCP\Files\Node[]
142
+     * @since 28.0.0
143
+     */
144
+    public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0);
145
+
146
+    /**
147
+     * get a file or folder inside the folder by its internal id
148
+     *
149
+     * This method could return multiple entries. For example once the file/folder
150
+     * is shared or mounted (files_external) to the user multiple times.
151
+     *
152
+     * Note that the different entries can have different permissions.
153
+     *
154
+     * @param int $id
155
+     * @return \OCP\Files\Node[]
156
+     * @since 6.0.0
157
+     */
158
+    public function getById($id);
159
+
160
+    /**
161
+     * get a file or folder inside the folder by its internal id
162
+     *
163
+     * Unlike getById, this method only returns a single node even if the user has
164
+     * access to the file with the requested id multiple times.
165
+     *
166
+     * This method provides no guarantee about which of the nodes in returned and the
167
+     * returned node might, for example, have less permissions than other nodes for the same file
168
+     *
169
+     * Apps that require accurate information about the users access to the file should use getById
170
+     * instead of pick the correct node out of the result.
171
+     *
172
+     * @param int $id
173
+     * @return Node|null
174
+     * @since 29.0.0
175
+     */
176
+    public function getFirstNodeById(int $id): ?Node;
177
+
178
+    /**
179
+     * Get the amount of free space inside the folder
180
+     *
181
+     * @return int
182
+     * @since 6.0.0
183
+     */
184
+    public function getFreeSpace();
185
+
186
+    /**
187
+     * Check if new files or folders can be created within the folder
188
+     *
189
+     * @return bool
190
+     * @since 6.0.0
191
+     */
192
+    public function isCreatable();
193
+
194
+    /**
195
+     * Add a suffix to the name in case the file exists
196
+     *
197
+     * @param string $filename
198
+     * @return string
199
+     * @throws NotPermittedException
200
+     * @since 8.1.0
201
+     */
202
+    public function getNonExistingName($filename);
203
+
204
+    /**
205
+     * @param int $limit
206
+     * @param int $offset
207
+     * @return \OCP\Files\Node[]
208
+     * @since 9.1.0
209
+     */
210
+    public function getRecent($limit, $offset = 0);
211
+
212
+    /**
213
+     * Verify if the given path is valid and allowed from this folder.
214
+     *
215
+     * @param string $path the path from this folder
216
+     * @param string $fileName
217
+     * @param bool $readonly Check only if the path is allowed for read-only access
218
+     * @throws InvalidPathException
219
+     * @since 32.0.0
220
+     */
221
+    public function verifyPath($fileName, $readonly = false): void;
222 222
 }
Please login to merge, or discard this patch.
lib/public/Files.php 1 patch
Indentation   +99 added lines, -99 removed lines patch added patch discarded remove patch
@@ -19,111 +19,111 @@
 block discarded – undo
19 19
  * @deprecated 14.0.0
20 20
  */
21 21
 class Files {
22
-	/**
23
-	 * Recursive deletion of folders
24
-	 *
25
-	 * @param string $dir path to the folder
26
-	 * @param bool $deleteSelf if set to false only the content of the folder will be deleted
27
-	 * @return bool
28
-	 * @since 5.0.0
29
-	 * @since 32.0.0 added the $deleteSelf parameter
30
-	 * @deprecated 14.0.0
31
-	 */
32
-	public static function rmdirr($dir, bool $deleteSelf = true) {
33
-		if (is_dir($dir)) {
34
-			$files = new \RecursiveIteratorIterator(
35
-				new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
36
-				\RecursiveIteratorIterator::CHILD_FIRST
37
-			);
22
+    /**
23
+     * Recursive deletion of folders
24
+     *
25
+     * @param string $dir path to the folder
26
+     * @param bool $deleteSelf if set to false only the content of the folder will be deleted
27
+     * @return bool
28
+     * @since 5.0.0
29
+     * @since 32.0.0 added the $deleteSelf parameter
30
+     * @deprecated 14.0.0
31
+     */
32
+    public static function rmdirr($dir, bool $deleteSelf = true) {
33
+        if (is_dir($dir)) {
34
+            $files = new \RecursiveIteratorIterator(
35
+                new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
36
+                \RecursiveIteratorIterator::CHILD_FIRST
37
+            );
38 38
 
39
-			foreach ($files as $fileInfo) {
40
-				/** @var \SplFileInfo $fileInfo */
41
-				if ($fileInfo->isLink()) {
42
-					unlink($fileInfo->getPathname());
43
-				} elseif ($fileInfo->isDir()) {
44
-					rmdir($fileInfo->getRealPath());
45
-				} else {
46
-					unlink($fileInfo->getRealPath());
47
-				}
48
-			}
49
-			if ($deleteSelf) {
50
-				rmdir($dir);
51
-			}
52
-		} elseif (file_exists($dir)) {
53
-			if ($deleteSelf) {
54
-				unlink($dir);
55
-			}
56
-		}
57
-		if (!$deleteSelf) {
58
-			return true;
59
-		}
39
+            foreach ($files as $fileInfo) {
40
+                /** @var \SplFileInfo $fileInfo */
41
+                if ($fileInfo->isLink()) {
42
+                    unlink($fileInfo->getPathname());
43
+                } elseif ($fileInfo->isDir()) {
44
+                    rmdir($fileInfo->getRealPath());
45
+                } else {
46
+                    unlink($fileInfo->getRealPath());
47
+                }
48
+            }
49
+            if ($deleteSelf) {
50
+                rmdir($dir);
51
+            }
52
+        } elseif (file_exists($dir)) {
53
+            if ($deleteSelf) {
54
+                unlink($dir);
55
+            }
56
+        }
57
+        if (!$deleteSelf) {
58
+            return true;
59
+        }
60 60
 
61
-		return !file_exists($dir);
62
-	}
61
+        return !file_exists($dir);
62
+    }
63 63
 
64
-	/**
65
-	 * Get the mimetype form a local file
66
-	 * @param string $path
67
-	 * @return string
68
-	 *                does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead
69
-	 * @since 5.0.0
70
-	 * @deprecated 14.0.0
71
-	 */
72
-	public static function getMimeType($path) {
73
-		return Server::get(IMimeTypeDetector::class)->detect($path);
74
-	}
64
+    /**
65
+     * Get the mimetype form a local file
66
+     * @param string $path
67
+     * @return string
68
+     *                does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead
69
+     * @since 5.0.0
70
+     * @deprecated 14.0.0
71
+     */
72
+    public static function getMimeType($path) {
73
+        return Server::get(IMimeTypeDetector::class)->detect($path);
74
+    }
75 75
 
76
-	/**
77
-	 * Search for files by mimetype
78
-	 * @param string $mimetype
79
-	 * @return array
80
-	 * @since 6.0.0
81
-	 * @deprecated 14.0.0
82
-	 */
83
-	public static function searchByMime($mimetype) {
84
-		return \OC\Files\Filesystem::searchByMime($mimetype);
85
-	}
76
+    /**
77
+     * Search for files by mimetype
78
+     * @param string $mimetype
79
+     * @return array
80
+     * @since 6.0.0
81
+     * @deprecated 14.0.0
82
+     */
83
+    public static function searchByMime($mimetype) {
84
+        return \OC\Files\Filesystem::searchByMime($mimetype);
85
+    }
86 86
 
87
-	/**
88
-	 * Copy the contents of one stream to another
89
-	 *
90
-	 * @template T of null|true
91
-	 * @param resource $source
92
-	 * @param resource $target
93
-	 * @param T $includeResult
94
-	 * @return int|array
95
-	 * @psalm-return (T is true ? array{0: int, 1: bool} : int)
96
-	 * @since 5.0.0
97
-	 * @since 32.0.0 added $includeResult parameter
98
-	 * @deprecated 14.0.0
99
-	 */
100
-	public static function streamCopy($source, $target, ?bool $includeResult = null) {
101
-		if (!$source || !$target) {
102
-			return $includeResult ? [0, false] : 0;
103
-		}
87
+    /**
88
+     * Copy the contents of one stream to another
89
+     *
90
+     * @template T of null|true
91
+     * @param resource $source
92
+     * @param resource $target
93
+     * @param T $includeResult
94
+     * @return int|array
95
+     * @psalm-return (T is true ? array{0: int, 1: bool} : int)
96
+     * @since 5.0.0
97
+     * @since 32.0.0 added $includeResult parameter
98
+     * @deprecated 14.0.0
99
+     */
100
+    public static function streamCopy($source, $target, ?bool $includeResult = null) {
101
+        if (!$source || !$target) {
102
+            return $includeResult ? [0, false] : 0;
103
+        }
104 104
 
105
-		$bufSize = 8192;
106
-		$count = 0;
107
-		$result = true;
108
-		while (!feof($source)) {
109
-			$buf = fread($source, $bufSize);
110
-			if ($buf === false) {
111
-				$result = false;
112
-				break;
113
-			}
105
+        $bufSize = 8192;
106
+        $count = 0;
107
+        $result = true;
108
+        while (!feof($source)) {
109
+            $buf = fread($source, $bufSize);
110
+            if ($buf === false) {
111
+                $result = false;
112
+                break;
113
+            }
114 114
 
115
-			$bytesWritten = fwrite($target, $buf);
116
-			if ($bytesWritten !== false) {
117
-				$count += $bytesWritten;
118
-			}
115
+            $bytesWritten = fwrite($target, $buf);
116
+            if ($bytesWritten !== false) {
117
+                $count += $bytesWritten;
118
+            }
119 119
 
120
-			if ($bytesWritten === false
121
-				|| ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
122
-			) {
123
-				$result = false;
124
-				break;
125
-			}
126
-		}
127
-		return $includeResult ? [$count, $result] : $count;
128
-	}
120
+            if ($bytesWritten === false
121
+                || ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
122
+            ) {
123
+                $result = false;
124
+                break;
125
+            }
126
+        }
127
+        return $includeResult ? [$count, $result] : $count;
128
+    }
129 129
 }
Please login to merge, or discard this patch.
lib/public/Share/IManager.php 1 patch
Indentation   +487 added lines, -487 removed lines patch added patch discarded remove patch
@@ -23,491 +23,491 @@
 block discarded – undo
23 23
  */
24 24
 #[Consumable(since: '9.0.0')]
25 25
 interface IManager {
26
-	/**
27
-	 * Create a Share
28
-	 *
29
-	 * @throws \Exception
30
-	 * @since 9.0.0
31
-	 */
32
-	public function createShare(IShare $share): IShare;
33
-
34
-	/**
35
-	 * Update a share.
36
-	 * The target of the share can't be changed this way: use moveShare
37
-	 * The share can't be removed this way (permission 0): use deleteShare
38
-	 * The state can't be changed this way: use acceptShare
39
-	 *
40
-	 * @param bool $onlyValid Only updates valid shares, invalid shares will be deleted automatically and are not updated
41
-	 * @throws \InvalidArgumentException
42
-	 * @since 9.0.0
43
-	 */
44
-	public function updateShare(IShare $share, bool $onlyValid = true): IShare;
45
-
46
-	/**
47
-	 * Accept a share.
48
-	 *
49
-	 * @throws \InvalidArgumentException
50
-	 * @since 18.0.0
51
-	 */
52
-	public function acceptShare(IShare $share, string $recipientId): IShare;
53
-
54
-	/**
55
-	 * Delete a share
56
-	 *
57
-	 * @throws ShareNotFound
58
-	 * @throws \InvalidArgumentException
59
-	 * @since 9.0.0
60
-	 */
61
-	public function deleteShare(IShare $share): void;
62
-
63
-	/**
64
-	 * Unshare a file as the recipient.
65
-	 * This can be different from a regular delete for example when one of
66
-	 * the users in a groups deletes that share. But the provider should
67
-	 * handle this.
68
-	 *
69
-	 * @since 9.0.0
70
-	 */
71
-	public function deleteFromSelf(IShare $share, string $recipientId): void;
72
-
73
-	/**
74
-	 * Restore the share when it has been deleted
75
-	 * Certain share types can be restored when they have been deleted
76
-	 * but the provider should properly handle this\
77
-	 *
78
-	 * @param IShare $share The share to restore
79
-	 * @param string $recipientId The user to restore the share for
80
-	 * @return IShare The restored share object
81
-	 * @throws GenericShareException In case restoring the share failed
82
-	 *
83
-	 * @since 14.0.0
84
-	 */
85
-	public function restoreShare(IShare $share, string $recipientId): IShare;
86
-
87
-	/**
88
-	 * Move the share as a recipient of the share.
89
-	 * This is updating the share target. So where the recipient has the share mounted.
90
-	 *
91
-	 * @throws \InvalidArgumentException If $share is a link share or the $recipient does not match
92
-	 * @since 9.0.0
93
-	 */
94
-	public function moveShare(IShare $share, string $recipientId): IShare;
95
-
96
-	/**
97
-	 * Get all shares shared by (initiated) by the provided user in a folder.
98
-	 *
99
-	 * @param string $userId
100
-	 * @param Folder $node
101
-	 * @param bool $reshares
102
-	 * @param bool $shallow Whether the method should stop at the first level, or look into sub-folders.
103
-	 * @return array<int, list<IShare>> [$fileId => IShare[], ...]
104
-	 * @since 11.0.0
105
-	 */
106
-	public function getSharesInFolder(string $userId, Folder $node, bool $reshares = false, bool $shallow = true): array;
107
-
108
-	/**
109
-	 * Get shares shared by (initiated) by the provided user.
110
-	 *
111
-	 * @param string $userId
112
-	 * @param IShare::TYPE_* $shareType
113
-	 * @param Node|null $path
114
-	 * @param bool $reshares
115
-	 * @param int $limit The maximum number of returned results, -1 for all results
116
-	 * @param int $offset
117
-	 * @param bool $onlyValid Only returns valid shares, invalid shares will be deleted automatically and are not returned
118
-	 * @return IShare[]
119
-	 * @since 9.0.0
120
-	 */
121
-	public function getSharesBy(string $userId, int $shareType, ?Node $path = null, bool $reshares = false, int $limit = 50, int $offset = 0, bool $onlyValid = true): array;
122
-
123
-	/**
124
-	 * Get shares shared with $user.
125
-	 * Filter by $node if provided
126
-	 *
127
-	 * @param string $userId
128
-	 * @param IShare::TYPE_* $shareType
129
-	 * @param Node|null $node
130
-	 * @param int $limit The maximum number of shares returned, -1 for all
131
-	 * @param int $offset
132
-	 * @return IShare[]
133
-	 * @since 9.0.0
134
-	 */
135
-	public function getSharedWith(string $userId, int $shareType, ?Node $node = null, int $limit = 50, int $offset = 0): array;
136
-
137
-	/**
138
-	 * Get deleted shares shared with $user.
139
-	 * Filter by $node if provided
140
-	 *
141
-	 * @param IShare::TYPE_* $shareType
142
-	 * @param int $limit The maximum number of shares returned, -1 for all
143
-	 * @return IShare[]
144
-	 * @since 14.0.0
145
-	 */
146
-	public function getDeletedSharedWith(string $userId, int $shareType, ?Node $node = null, int $limit = 50, int $offset = 0): array;
147
-
148
-	/**
149
-	 * Retrieve a share by the share id.
150
-	 * If the recipient is set make sure to retrieve the file for that user.
151
-	 * This makes sure that if a user has moved/deleted a group share this
152
-	 * is reflected.
153
-	 *
154
-	 * @param string|null $recipient userID of the recipient
155
-	 * @param bool $onlyValid Only returns valid shares, invalid shares will be deleted automatically and are not returned
156
-	 * @throws ShareNotFound
157
-	 * @since 9.0.0
158
-	 */
159
-	public function getShareById(string $id, ?string $recipient = null, bool $onlyValid = true): IShare;
160
-
161
-	/**
162
-	 * Get the share by token possible with password
163
-	 *
164
-	 * @throws ShareNotFound
165
-	 * @since 9.0.0
166
-	 */
167
-	public function getShareByToken(string $token): IShare;
168
-
169
-	/**
170
-	 * Verify the password of a public share
171
-	 *
172
-	 * @since 9.0.0
173
-	 */
174
-	public function checkPassword(IShare $share, ?string $password): bool;
175
-
176
-	/**
177
-	 * The user with UID is deleted.
178
-	 * All share providers have to cleanup the shares with this user as well
179
-	 * as shares owned by this user.
180
-	 * Shares only initiated by this user are fine.
181
-	 *
182
-	 * @since 9.1.0
183
-	 */
184
-	public function userDeleted(string $uid): void;
185
-
186
-	/**
187
-	 * The group with $gid is deleted
188
-	 * We need to clear up all shares to this group
189
-	 *
190
-	 * @since 9.1.0
191
-	 */
192
-	public function groupDeleted(string $gid): void;
193
-
194
-	/**
195
-	 * The user $uid is deleted from the group $gid
196
-	 * All user specific group shares have to be removed
197
-	 *
198
-	 * @since 9.1.0
199
-	 */
200
-	public function userDeletedFromGroup(string $uid, string $gid): void;
201
-
202
-	/**
203
-	 * Get access list to a path. This means
204
-	 * all the users that can access a given path.
205
-	 *
206
-	 * Consider:
207
-	 * -root
208
-	 * |-folder1 (23)
209
-	 *  |-folder2 (32)
210
-	 *   |-fileA (42)
211
-	 *
212
-	 * fileA is shared with user1 and user1@server1 and email1@maildomain1
213
-	 * folder2 is shared with group2 (user4 is a member of group2)
214
-	 * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
215
-	 *                        and email2@maildomain2
216
-	 *
217
-	 * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
218
-	 * [
219
-	 *  users  => [
220
-	 *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
221
-	 *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
222
-	 *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
223
-	 *  ],
224
-	 *  remote => [
225
-	 *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
226
-	 *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
227
-	 *  ],
228
-	 *  public => bool
229
-	 *  mail => [
230
-	 *      'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'],
231
-	 *      'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'],
232
-	 *  ]
233
-	 *
234
-	 * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
235
-	 * [
236
-	 *  users  => ['user1', 'user2', 'user4'],
237
-	 *  remote => bool,
238
-	 *  public => bool
239
-	 *  mail => ['email1@maildomain1', 'email2@maildomain2']
240
-	 * ]
241
-	 *
242
-	 * This is required for encryption/activity
243
-	 *
244
-	 * @param bool $recursive Should we check all parent folders as well
245
-	 * @param bool $currentAccess Should the user have currently access to the file
246
-	 * @return ($currentAccess is true
247
-	 * 		? array{
248
-	 *     		users?: array<string, array{node_id: int, node_path: string}>,
249
-	 *     		remote?: array<string, array{node_id: int, node_path: string}>,
250
-	 *     		public?: bool,
251
-	 *     		mail?: array<string, array{node_id: int, node_path: string}>
252
-	 *     	}
253
-	 *      : array{users?: list<string>, remote?: bool, public?: bool, mail?: list<string>})
254
-	 * @since 12.0.0
255
-	 */
256
-	public function getAccessList(Node $path, bool $recursive = true, bool $currentAccess = false): array;
257
-
258
-	/**
259
-	 * Instantiates a new share object. This is to be passed to
260
-	 * createShare.
261
-	 *
262
-	 * @since 9.0.0
263
-	 */
264
-	public function newShare(): IShare;
265
-
266
-	/**
267
-	 * Is the share API enabled
268
-	 *
269
-	 * @since 9.0.0
270
-	 */
271
-	public function shareApiEnabled(): bool;
272
-
273
-	/**
274
-	 * Is public link sharing enabled
275
-	 *
276
-	 * @param ?IUser $user User to check against group exclusions, defaults to current session user
277
-	 * @return bool
278
-	 * @since 9.0.0
279
-	 * @since 33.0.0 Added optional $user parameter
280
-	 */
281
-	public function shareApiAllowLinks(): bool;
282
-
283
-	/**
284
-	 * Is password on public link required
285
-	 *
286
-	 * @param bool $checkGroupMembership Check group membership exclusion
287
-	 * @since 9.0.0
288
-	 * @since 24.0.0 Added optional $checkGroupMembership parameter
289
-	 */
290
-	public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true): bool;
291
-
292
-	/**
293
-	 * Is default expire date enabled
294
-	 *
295
-	 * @since 9.0.0
296
-	 */
297
-	public function shareApiLinkDefaultExpireDate(): bool;
298
-
299
-	/**
300
-	 * Is default expire date enforced
301
-	 *`
302
-	 * @since 9.0.0
303
-	 */
304
-	public function shareApiLinkDefaultExpireDateEnforced(): bool;
305
-
306
-	/**
307
-	 * Number of default expire days
308
-	 *
309
-	 * @since 9.0.0
310
-	 */
311
-	public function shareApiLinkDefaultExpireDays(): int;
312
-
313
-	/**
314
-	 * Is default internal expire date enabled
315
-	 *
316
-	 * @since 22.0.0
317
-	 */
318
-	public function shareApiInternalDefaultExpireDate(): bool;
319
-
320
-	/**
321
-	 * Is default remote expire date enabled
322
-	 *
323
-	 * @since 22.0.0
324
-	 */
325
-	public function shareApiRemoteDefaultExpireDate(): bool;
326
-
327
-	/**
328
-	 * Is default expire date enforced
329
-	 *
330
-	 * @since 22.0.0
331
-	 */
332
-	public function shareApiInternalDefaultExpireDateEnforced(): bool;
333
-
334
-	/**
335
-	 * Is default expire date enforced for remote shares
336
-	 *
337
-	 * @since 22.0.0
338
-	 */
339
-	public function shareApiRemoteDefaultExpireDateEnforced(): bool;
340
-
341
-	/**
342
-	 * Number of default expire days
343
-	 *
344
-	 * @since 22.0.0
345
-	 */
346
-	public function shareApiInternalDefaultExpireDays(): int;
347
-
348
-	/**
349
-	 * Number of default expire days for remote shares
350
-	 *
351
-	 * @since 22.0.0
352
-	 */
353
-	public function shareApiRemoteDefaultExpireDays(): int;
354
-
355
-	/**
356
-	 * Allow public upload on link shares
357
-	 *
358
-	 * @since 9.0.0
359
-	 */
360
-	public function shareApiLinkAllowPublicUpload(): bool;
361
-
362
-	/**
363
-	 * Check if user can only share with group members.
364
-	 *
365
-	 * @since 9.0.0
366
-	 */
367
-	public function shareWithGroupMembersOnly(): bool;
368
-
369
-	/**
370
-	 * If shareWithGroupMembersOnly is enabled, return an optional
371
-	 * list of groups that must be excluded from the principle of
372
-	 * belonging to the same group.
373
-	 * @return array
374
-	 * @since 27.0.0
375
-	 */
376
-	public function shareWithGroupMembersOnlyExcludeGroupsList(): array;
377
-
378
-	/**
379
-	 * Check if users can share with groups
380
-	 *
381
-	 * @since 9.0.1
382
-	 */
383
-	public function allowGroupSharing(): bool;
384
-
385
-	/**
386
-	 * Check if user enumeration is allowed
387
-	 *
388
-	 * @since 19.0.0
389
-	 */
390
-	public function allowEnumeration(): bool;
391
-
392
-	/**
393
-	 * Check if user enumeration is limited to the users groups
394
-	 *
395
-	 * @since 19.0.0
396
-	 */
397
-	public function limitEnumerationToGroups(): bool;
398
-
399
-	/**
400
-	 * Check if user enumeration is limited to the phonebook matches
401
-	 *
402
-	 * @since 21.0.1
403
-	 */
404
-	public function limitEnumerationToPhone(): bool;
405
-
406
-	/**
407
-	 * Check if user enumeration is allowed to return also on full match
408
-	 * and ignore limitations to phonebook or groups.
409
-	 *
410
-	 * @since 21.0.1
411
-	 */
412
-	public function allowEnumerationFullMatch(): bool;
413
-
414
-	/**
415
-	 * When `allowEnumerationFullMatch` is enabled and `matchEmail` is set,
416
-	 * then also return results for full email matches.
417
-	 *
418
-	 * @since 25.0.0
419
-	 */
420
-	public function matchEmail(): bool;
421
-
422
-	/**
423
-	 * When `allowEnumerationFullMatch` is enabled and `matchUserId` is set,
424
-	 * then also return results for full user id matches.
425
-	 *
426
-	 * @since 33.0.0
427
-	 */
428
-	public function matchUserId(): bool;
429
-
430
-	/**
431
-	 * When `allowEnumerationFullMatch` is enabled and `ignoreSecondDisplayName` is set,
432
-	 * then the search should ignore matches on the second displayname and only use the first.
433
-	 *
434
-	 * @since 25.0.0
435
-	 */
436
-	public function ignoreSecondDisplayName(): bool;
437
-
438
-
439
-	/**
440
-	 * Check if custom tokens are allowed
441
-	 *
442
-	 * @since 31.0.0
443
-	 */
444
-	public function allowCustomTokens(): bool;
445
-
446
-	/**
447
-	 * Check if the current user can view the share
448
-	 * even if the download is disabled.
449
-	 *
450
-	 * @since 32.0.0
451
-	 */
452
-	public function allowViewWithoutDownload(): bool;
453
-
454
-	/**
455
-	 * Check if the current user can enumerate the target user
456
-	 *
457
-	 * @since 23.0.0
458
-	 */
459
-	public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool;
460
-
461
-	/**
462
-	 * Check if sharing is disabled for the given user
463
-	 *
464
-	 * @since 9.0.0
465
-	 */
466
-	public function sharingDisabledForUser(?string $userId): bool;
467
-
468
-	/**
469
-	 * Check if outgoing server2server shares are allowed
470
-	 * @since 9.0.0
471
-	 */
472
-	public function outgoingServer2ServerSharesAllowed(): bool;
473
-
474
-	/**
475
-	 * Check if outgoing server2server shares are allowed
476
-	 * @since 14.0.0
477
-	 */
478
-	public function outgoingServer2ServerGroupSharesAllowed(): bool;
479
-
480
-
481
-	/**
482
-	 * Check if a given share provider exists
483
-	 * @param IShare::TYPE_* $shareType
484
-	 * @since 11.0.0
485
-	 */
486
-	public function shareProviderExists(int $shareType): bool;
487
-
488
-	/**
489
-	 * @param string $shareProviderClass
490
-	 * @since 21.0.0
491
-	 */
492
-	public function registerShareProvider(string $shareProviderClass): void;
493
-
494
-	/**
495
-	 * @Internal
496
-	 *
497
-	 * Get all the shares as iterable to reduce memory overhead
498
-	 * Note, since this opens up database cursors the iterable should
499
-	 * be fully iterated.
500
-	 *
501
-	 * @return iterable<IShare>
502
-	 * @since 18.0.0
503
-	 */
504
-	public function getAllShares(): iterable;
505
-
506
-	/**
507
-	 * Generate a unique share token
508
-	 *
509
-	 * @throws ShareTokenException Failed to generate a unique token
510
-	 * @since 31.0.0
511
-	 */
512
-	public function generateToken(): string;
26
+    /**
27
+     * Create a Share
28
+     *
29
+     * @throws \Exception
30
+     * @since 9.0.0
31
+     */
32
+    public function createShare(IShare $share): IShare;
33
+
34
+    /**
35
+     * Update a share.
36
+     * The target of the share can't be changed this way: use moveShare
37
+     * The share can't be removed this way (permission 0): use deleteShare
38
+     * The state can't be changed this way: use acceptShare
39
+     *
40
+     * @param bool $onlyValid Only updates valid shares, invalid shares will be deleted automatically and are not updated
41
+     * @throws \InvalidArgumentException
42
+     * @since 9.0.0
43
+     */
44
+    public function updateShare(IShare $share, bool $onlyValid = true): IShare;
45
+
46
+    /**
47
+     * Accept a share.
48
+     *
49
+     * @throws \InvalidArgumentException
50
+     * @since 18.0.0
51
+     */
52
+    public function acceptShare(IShare $share, string $recipientId): IShare;
53
+
54
+    /**
55
+     * Delete a share
56
+     *
57
+     * @throws ShareNotFound
58
+     * @throws \InvalidArgumentException
59
+     * @since 9.0.0
60
+     */
61
+    public function deleteShare(IShare $share): void;
62
+
63
+    /**
64
+     * Unshare a file as the recipient.
65
+     * This can be different from a regular delete for example when one of
66
+     * the users in a groups deletes that share. But the provider should
67
+     * handle this.
68
+     *
69
+     * @since 9.0.0
70
+     */
71
+    public function deleteFromSelf(IShare $share, string $recipientId): void;
72
+
73
+    /**
74
+     * Restore the share when it has been deleted
75
+     * Certain share types can be restored when they have been deleted
76
+     * but the provider should properly handle this\
77
+     *
78
+     * @param IShare $share The share to restore
79
+     * @param string $recipientId The user to restore the share for
80
+     * @return IShare The restored share object
81
+     * @throws GenericShareException In case restoring the share failed
82
+     *
83
+     * @since 14.0.0
84
+     */
85
+    public function restoreShare(IShare $share, string $recipientId): IShare;
86
+
87
+    /**
88
+     * Move the share as a recipient of the share.
89
+     * This is updating the share target. So where the recipient has the share mounted.
90
+     *
91
+     * @throws \InvalidArgumentException If $share is a link share or the $recipient does not match
92
+     * @since 9.0.0
93
+     */
94
+    public function moveShare(IShare $share, string $recipientId): IShare;
95
+
96
+    /**
97
+     * Get all shares shared by (initiated) by the provided user in a folder.
98
+     *
99
+     * @param string $userId
100
+     * @param Folder $node
101
+     * @param bool $reshares
102
+     * @param bool $shallow Whether the method should stop at the first level, or look into sub-folders.
103
+     * @return array<int, list<IShare>> [$fileId => IShare[], ...]
104
+     * @since 11.0.0
105
+     */
106
+    public function getSharesInFolder(string $userId, Folder $node, bool $reshares = false, bool $shallow = true): array;
107
+
108
+    /**
109
+     * Get shares shared by (initiated) by the provided user.
110
+     *
111
+     * @param string $userId
112
+     * @param IShare::TYPE_* $shareType
113
+     * @param Node|null $path
114
+     * @param bool $reshares
115
+     * @param int $limit The maximum number of returned results, -1 for all results
116
+     * @param int $offset
117
+     * @param bool $onlyValid Only returns valid shares, invalid shares will be deleted automatically and are not returned
118
+     * @return IShare[]
119
+     * @since 9.0.0
120
+     */
121
+    public function getSharesBy(string $userId, int $shareType, ?Node $path = null, bool $reshares = false, int $limit = 50, int $offset = 0, bool $onlyValid = true): array;
122
+
123
+    /**
124
+     * Get shares shared with $user.
125
+     * Filter by $node if provided
126
+     *
127
+     * @param string $userId
128
+     * @param IShare::TYPE_* $shareType
129
+     * @param Node|null $node
130
+     * @param int $limit The maximum number of shares returned, -1 for all
131
+     * @param int $offset
132
+     * @return IShare[]
133
+     * @since 9.0.0
134
+     */
135
+    public function getSharedWith(string $userId, int $shareType, ?Node $node = null, int $limit = 50, int $offset = 0): array;
136
+
137
+    /**
138
+     * Get deleted shares shared with $user.
139
+     * Filter by $node if provided
140
+     *
141
+     * @param IShare::TYPE_* $shareType
142
+     * @param int $limit The maximum number of shares returned, -1 for all
143
+     * @return IShare[]
144
+     * @since 14.0.0
145
+     */
146
+    public function getDeletedSharedWith(string $userId, int $shareType, ?Node $node = null, int $limit = 50, int $offset = 0): array;
147
+
148
+    /**
149
+     * Retrieve a share by the share id.
150
+     * If the recipient is set make sure to retrieve the file for that user.
151
+     * This makes sure that if a user has moved/deleted a group share this
152
+     * is reflected.
153
+     *
154
+     * @param string|null $recipient userID of the recipient
155
+     * @param bool $onlyValid Only returns valid shares, invalid shares will be deleted automatically and are not returned
156
+     * @throws ShareNotFound
157
+     * @since 9.0.0
158
+     */
159
+    public function getShareById(string $id, ?string $recipient = null, bool $onlyValid = true): IShare;
160
+
161
+    /**
162
+     * Get the share by token possible with password
163
+     *
164
+     * @throws ShareNotFound
165
+     * @since 9.0.0
166
+     */
167
+    public function getShareByToken(string $token): IShare;
168
+
169
+    /**
170
+     * Verify the password of a public share
171
+     *
172
+     * @since 9.0.0
173
+     */
174
+    public function checkPassword(IShare $share, ?string $password): bool;
175
+
176
+    /**
177
+     * The user with UID is deleted.
178
+     * All share providers have to cleanup the shares with this user as well
179
+     * as shares owned by this user.
180
+     * Shares only initiated by this user are fine.
181
+     *
182
+     * @since 9.1.0
183
+     */
184
+    public function userDeleted(string $uid): void;
185
+
186
+    /**
187
+     * The group with $gid is deleted
188
+     * We need to clear up all shares to this group
189
+     *
190
+     * @since 9.1.0
191
+     */
192
+    public function groupDeleted(string $gid): void;
193
+
194
+    /**
195
+     * The user $uid is deleted from the group $gid
196
+     * All user specific group shares have to be removed
197
+     *
198
+     * @since 9.1.0
199
+     */
200
+    public function userDeletedFromGroup(string $uid, string $gid): void;
201
+
202
+    /**
203
+     * Get access list to a path. This means
204
+     * all the users that can access a given path.
205
+     *
206
+     * Consider:
207
+     * -root
208
+     * |-folder1 (23)
209
+     *  |-folder2 (32)
210
+     *   |-fileA (42)
211
+     *
212
+     * fileA is shared with user1 and user1@server1 and email1@maildomain1
213
+     * folder2 is shared with group2 (user4 is a member of group2)
214
+     * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
215
+     *                        and email2@maildomain2
216
+     *
217
+     * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
218
+     * [
219
+     *  users  => [
220
+     *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
221
+     *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
222
+     *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
223
+     *  ],
224
+     *  remote => [
225
+     *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
226
+     *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
227
+     *  ],
228
+     *  public => bool
229
+     *  mail => [
230
+     *      'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'],
231
+     *      'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'],
232
+     *  ]
233
+     *
234
+     * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
235
+     * [
236
+     *  users  => ['user1', 'user2', 'user4'],
237
+     *  remote => bool,
238
+     *  public => bool
239
+     *  mail => ['email1@maildomain1', 'email2@maildomain2']
240
+     * ]
241
+     *
242
+     * This is required for encryption/activity
243
+     *
244
+     * @param bool $recursive Should we check all parent folders as well
245
+     * @param bool $currentAccess Should the user have currently access to the file
246
+     * @return ($currentAccess is true
247
+     * 		? array{
248
+     *     		users?: array<string, array{node_id: int, node_path: string}>,
249
+     *     		remote?: array<string, array{node_id: int, node_path: string}>,
250
+     *     		public?: bool,
251
+     *     		mail?: array<string, array{node_id: int, node_path: string}>
252
+     *     	}
253
+     *      : array{users?: list<string>, remote?: bool, public?: bool, mail?: list<string>})
254
+     * @since 12.0.0
255
+     */
256
+    public function getAccessList(Node $path, bool $recursive = true, bool $currentAccess = false): array;
257
+
258
+    /**
259
+     * Instantiates a new share object. This is to be passed to
260
+     * createShare.
261
+     *
262
+     * @since 9.0.0
263
+     */
264
+    public function newShare(): IShare;
265
+
266
+    /**
267
+     * Is the share API enabled
268
+     *
269
+     * @since 9.0.0
270
+     */
271
+    public function shareApiEnabled(): bool;
272
+
273
+    /**
274
+     * Is public link sharing enabled
275
+     *
276
+     * @param ?IUser $user User to check against group exclusions, defaults to current session user
277
+     * @return bool
278
+     * @since 9.0.0
279
+     * @since 33.0.0 Added optional $user parameter
280
+     */
281
+    public function shareApiAllowLinks(): bool;
282
+
283
+    /**
284
+     * Is password on public link required
285
+     *
286
+     * @param bool $checkGroupMembership Check group membership exclusion
287
+     * @since 9.0.0
288
+     * @since 24.0.0 Added optional $checkGroupMembership parameter
289
+     */
290
+    public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true): bool;
291
+
292
+    /**
293
+     * Is default expire date enabled
294
+     *
295
+     * @since 9.0.0
296
+     */
297
+    public function shareApiLinkDefaultExpireDate(): bool;
298
+
299
+    /**
300
+     * Is default expire date enforced
301
+     *`
302
+     * @since 9.0.0
303
+     */
304
+    public function shareApiLinkDefaultExpireDateEnforced(): bool;
305
+
306
+    /**
307
+     * Number of default expire days
308
+     *
309
+     * @since 9.0.0
310
+     */
311
+    public function shareApiLinkDefaultExpireDays(): int;
312
+
313
+    /**
314
+     * Is default internal expire date enabled
315
+     *
316
+     * @since 22.0.0
317
+     */
318
+    public function shareApiInternalDefaultExpireDate(): bool;
319
+
320
+    /**
321
+     * Is default remote expire date enabled
322
+     *
323
+     * @since 22.0.0
324
+     */
325
+    public function shareApiRemoteDefaultExpireDate(): bool;
326
+
327
+    /**
328
+     * Is default expire date enforced
329
+     *
330
+     * @since 22.0.0
331
+     */
332
+    public function shareApiInternalDefaultExpireDateEnforced(): bool;
333
+
334
+    /**
335
+     * Is default expire date enforced for remote shares
336
+     *
337
+     * @since 22.0.0
338
+     */
339
+    public function shareApiRemoteDefaultExpireDateEnforced(): bool;
340
+
341
+    /**
342
+     * Number of default expire days
343
+     *
344
+     * @since 22.0.0
345
+     */
346
+    public function shareApiInternalDefaultExpireDays(): int;
347
+
348
+    /**
349
+     * Number of default expire days for remote shares
350
+     *
351
+     * @since 22.0.0
352
+     */
353
+    public function shareApiRemoteDefaultExpireDays(): int;
354
+
355
+    /**
356
+     * Allow public upload on link shares
357
+     *
358
+     * @since 9.0.0
359
+     */
360
+    public function shareApiLinkAllowPublicUpload(): bool;
361
+
362
+    /**
363
+     * Check if user can only share with group members.
364
+     *
365
+     * @since 9.0.0
366
+     */
367
+    public function shareWithGroupMembersOnly(): bool;
368
+
369
+    /**
370
+     * If shareWithGroupMembersOnly is enabled, return an optional
371
+     * list of groups that must be excluded from the principle of
372
+     * belonging to the same group.
373
+     * @return array
374
+     * @since 27.0.0
375
+     */
376
+    public function shareWithGroupMembersOnlyExcludeGroupsList(): array;
377
+
378
+    /**
379
+     * Check if users can share with groups
380
+     *
381
+     * @since 9.0.1
382
+     */
383
+    public function allowGroupSharing(): bool;
384
+
385
+    /**
386
+     * Check if user enumeration is allowed
387
+     *
388
+     * @since 19.0.0
389
+     */
390
+    public function allowEnumeration(): bool;
391
+
392
+    /**
393
+     * Check if user enumeration is limited to the users groups
394
+     *
395
+     * @since 19.0.0
396
+     */
397
+    public function limitEnumerationToGroups(): bool;
398
+
399
+    /**
400
+     * Check if user enumeration is limited to the phonebook matches
401
+     *
402
+     * @since 21.0.1
403
+     */
404
+    public function limitEnumerationToPhone(): bool;
405
+
406
+    /**
407
+     * Check if user enumeration is allowed to return also on full match
408
+     * and ignore limitations to phonebook or groups.
409
+     *
410
+     * @since 21.0.1
411
+     */
412
+    public function allowEnumerationFullMatch(): bool;
413
+
414
+    /**
415
+     * When `allowEnumerationFullMatch` is enabled and `matchEmail` is set,
416
+     * then also return results for full email matches.
417
+     *
418
+     * @since 25.0.0
419
+     */
420
+    public function matchEmail(): bool;
421
+
422
+    /**
423
+     * When `allowEnumerationFullMatch` is enabled and `matchUserId` is set,
424
+     * then also return results for full user id matches.
425
+     *
426
+     * @since 33.0.0
427
+     */
428
+    public function matchUserId(): bool;
429
+
430
+    /**
431
+     * When `allowEnumerationFullMatch` is enabled and `ignoreSecondDisplayName` is set,
432
+     * then the search should ignore matches on the second displayname and only use the first.
433
+     *
434
+     * @since 25.0.0
435
+     */
436
+    public function ignoreSecondDisplayName(): bool;
437
+
438
+
439
+    /**
440
+     * Check if custom tokens are allowed
441
+     *
442
+     * @since 31.0.0
443
+     */
444
+    public function allowCustomTokens(): bool;
445
+
446
+    /**
447
+     * Check if the current user can view the share
448
+     * even if the download is disabled.
449
+     *
450
+     * @since 32.0.0
451
+     */
452
+    public function allowViewWithoutDownload(): bool;
453
+
454
+    /**
455
+     * Check if the current user can enumerate the target user
456
+     *
457
+     * @since 23.0.0
458
+     */
459
+    public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool;
460
+
461
+    /**
462
+     * Check if sharing is disabled for the given user
463
+     *
464
+     * @since 9.0.0
465
+     */
466
+    public function sharingDisabledForUser(?string $userId): bool;
467
+
468
+    /**
469
+     * Check if outgoing server2server shares are allowed
470
+     * @since 9.0.0
471
+     */
472
+    public function outgoingServer2ServerSharesAllowed(): bool;
473
+
474
+    /**
475
+     * Check if outgoing server2server shares are allowed
476
+     * @since 14.0.0
477
+     */
478
+    public function outgoingServer2ServerGroupSharesAllowed(): bool;
479
+
480
+
481
+    /**
482
+     * Check if a given share provider exists
483
+     * @param IShare::TYPE_* $shareType
484
+     * @since 11.0.0
485
+     */
486
+    public function shareProviderExists(int $shareType): bool;
487
+
488
+    /**
489
+     * @param string $shareProviderClass
490
+     * @since 21.0.0
491
+     */
492
+    public function registerShareProvider(string $shareProviderClass): void;
493
+
494
+    /**
495
+     * @Internal
496
+     *
497
+     * Get all the shares as iterable to reduce memory overhead
498
+     * Note, since this opens up database cursors the iterable should
499
+     * be fully iterated.
500
+     *
501
+     * @return iterable<IShare>
502
+     * @since 18.0.0
503
+     */
504
+    public function getAllShares(): iterable;
505
+
506
+    /**
507
+     * Generate a unique share token
508
+     *
509
+     * @throws ShareTokenException Failed to generate a unique token
510
+     * @since 31.0.0
511
+     */
512
+    public function generateToken(): string;
513 513
 }
Please login to merge, or discard this patch.
lib/public/Share/IShareProvider.php 1 patch
Indentation   +198 added lines, -198 removed lines patch added patch discarded remove patch
@@ -18,202 +18,202 @@
 block discarded – undo
18 18
  * @since 9.0.0
19 19
  */
20 20
 interface IShareProvider {
21
-	/**
22
-	 * Return the identifier of this provider.
23
-	 *
24
-	 * @return string Containing only [a-zA-Z0-9]
25
-	 * @since 9.0.0
26
-	 */
27
-	public function identifier();
28
-
29
-	/**
30
-	 * Create a share
31
-	 *
32
-	 * @param \OCP\Share\IShare $share
33
-	 * @return \OCP\Share\IShare The share object
34
-	 * @since 9.0.0
35
-	 */
36
-	public function create(\OCP\Share\IShare $share);
37
-
38
-	/**
39
-	 * Update a share
40
-	 *
41
-	 * @param \OCP\Share\IShare $share
42
-	 * @return \OCP\Share\IShare The share object
43
-	 * @since 9.0.0
44
-	 */
45
-	public function update(\OCP\Share\IShare $share);
46
-
47
-	/**
48
-	 * Delete a share
49
-	 *
50
-	 * @param \OCP\Share\IShare $share
51
-	 * @since 9.0.0
52
-	 */
53
-	public function delete(\OCP\Share\IShare $share);
54
-
55
-	/**
56
-	 * Unshare a file from self as recipient.
57
-	 * This may require special handling. If a user unshares a group
58
-	 * share from their self then the original group share should still exist.
59
-	 *
60
-	 * @param \OCP\Share\IShare $share
61
-	 * @param string $recipient UserId of the recipient
62
-	 * @since 9.0.0
63
-	 */
64
-	public function deleteFromSelf(\OCP\Share\IShare $share, $recipient);
65
-
66
-	/**
67
-	 * Restore a share for a given recipient. The implementation could be provider independant.
68
-	 *
69
-	 * @param IShare $share
70
-	 * @param string $recipient
71
-	 * @return IShare The restored share object
72
-	 *
73
-	 * @since 14.0.0
74
-	 * @throws GenericShareException In case the share could not be restored
75
-	 */
76
-	public function restore(IShare $share, string $recipient): IShare;
77
-
78
-	/**
79
-	 * Move a share as a recipient.
80
-	 * This is updating the share target. Thus the mount point of the recipient.
81
-	 * This may require special handling. If a user moves a group share
82
-	 * the target should only be changed for them.
83
-	 *
84
-	 * @param \OCP\Share\IShare $share
85
-	 * @param string $recipient userId of recipient
86
-	 * @return \OCP\Share\IShare
87
-	 * @since 9.0.0
88
-	 */
89
-	public function move(\OCP\Share\IShare $share, $recipient);
90
-
91
-	/**
92
-	 * Get all shares by the given user in a folder
93
-	 *
94
-	 * @param string $userId
95
-	 * @param Folder $node
96
-	 * @param bool $reshares Also get the shares where $user is the owner instead of just the shares where $user is the initiator
97
-	 * @param bool $shallow Whether the method should stop at the first level, or look into sub-folders.
98
-	 * @return array<int, list<\OCP\Share\IShare>>
99
-	 * @since 11.0.0
100
-	 */
101
-	public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true);
102
-
103
-	/**
104
-	 * Get all shares by the given user
105
-	 *
106
-	 * @param string $userId
107
-	 * @param int $shareType
108
-	 * @param Node|null $node
109
-	 * @param bool $reshares Also get the shares where $user is the owner instead of just the shares where $user is the initiator
110
-	 * @param int $limit The maximum number of shares to be returned, -1 for all shares
111
-	 * @param int $offset
112
-	 * @return \OCP\Share\IShare[]
113
-	 * @since 9.0.0
114
-	 */
115
-	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset);
116
-
117
-	/**
118
-	 * Get share by id
119
-	 *
120
-	 * @param string $id
121
-	 * @param string|null $recipientId
122
-	 * @return \OCP\Share\IShare
123
-	 * @throws ShareNotFound
124
-	 * @since 9.0.0
125
-	 */
126
-	public function getShareById($id, $recipientId = null);
127
-
128
-	/**
129
-	 * Get shares for a given path
130
-	 *
131
-	 * @param Node $path
132
-	 * @return \OCP\Share\IShare[]
133
-	 * @since 9.0.0
134
-	 */
135
-	public function getSharesByPath(Node $path);
136
-
137
-	/**
138
-	 * Get shared with the given user
139
-	 *
140
-	 * @param string $userId get shares where this user is the recipient
141
-	 * @param int $shareType
142
-	 * @param Node|null $node
143
-	 * @param int $limit The max number of entries returned, -1 for all
144
-	 * @param int $offset
145
-	 * @return \OCP\Share\IShare[]
146
-	 * @since 9.0.0
147
-	 */
148
-	public function getSharedWith($userId, $shareType, $node, $limit, $offset);
149
-
150
-	/**
151
-	 * Get a share by token
152
-	 *
153
-	 * @param string $token
154
-	 * @return \OCP\Share\IShare
155
-	 * @throws ShareNotFound
156
-	 * @since 9.0.0
157
-	 */
158
-	public function getShareByToken(string $token);
159
-
160
-	/**
161
-	 * A user is deleted from the system
162
-	 * So clean up the relevant shares.
163
-	 *
164
-	 * @param string $uid
165
-	 * @param int $shareType
166
-	 * @since 9.1.0
167
-	 */
168
-	public function userDeleted($uid, $shareType);
169
-
170
-	/**
171
-	 * A group is deleted from the system.
172
-	 * We have to clean up all shares to this group.
173
-	 * Providers not handling group shares should just return
174
-	 *
175
-	 * @param string $gid
176
-	 * @since 9.1.0
177
-	 */
178
-	public function groupDeleted($gid);
179
-
180
-	/**
181
-	 * A user is deleted from a group
182
-	 * We have to clean up all the related user specific group shares
183
-	 * Providers not handling group shares should just return
184
-	 *
185
-	 * @param string $uid
186
-	 * @param string $gid
187
-	 * @since 9.1.0
188
-	 */
189
-	public function userDeletedFromGroup($uid, $gid);
190
-
191
-	/**
192
-	 * Get the access list to the array of provided nodes.
193
-	 *
194
-	 * @see IManager::getAccessList() for sample docs
195
-	 *
196
-	 * @param Node[] $nodes The list of nodes to get access for
197
-	 * @param bool $currentAccess If current access is required (like for removed shares that might get revived later)
198
-	 * @return array
199
-	 * @since 12
200
-	 */
201
-	public function getAccessList($nodes, $currentAccess);
202
-
203
-	/**
204
-	 * Get all the shares in this provider returned as iterable to reduce memory
205
-	 * overhead
206
-	 *
207
-	 * @return iterable<IShare>
208
-	 * @since 18.0.0
209
-	 */
210
-	public function getAllShares(): iterable;
211
-
212
-	/**
213
-	 * Get all children of this share
214
-	 *
215
-	 * @return IShare[]
216
-	 * @since 9.0.0
217
-	 */
218
-	public function getChildren(IShare $parent);
21
+    /**
22
+     * Return the identifier of this provider.
23
+     *
24
+     * @return string Containing only [a-zA-Z0-9]
25
+     * @since 9.0.0
26
+     */
27
+    public function identifier();
28
+
29
+    /**
30
+     * Create a share
31
+     *
32
+     * @param \OCP\Share\IShare $share
33
+     * @return \OCP\Share\IShare The share object
34
+     * @since 9.0.0
35
+     */
36
+    public function create(\OCP\Share\IShare $share);
37
+
38
+    /**
39
+     * Update a share
40
+     *
41
+     * @param \OCP\Share\IShare $share
42
+     * @return \OCP\Share\IShare The share object
43
+     * @since 9.0.0
44
+     */
45
+    public function update(\OCP\Share\IShare $share);
46
+
47
+    /**
48
+     * Delete a share
49
+     *
50
+     * @param \OCP\Share\IShare $share
51
+     * @since 9.0.0
52
+     */
53
+    public function delete(\OCP\Share\IShare $share);
54
+
55
+    /**
56
+     * Unshare a file from self as recipient.
57
+     * This may require special handling. If a user unshares a group
58
+     * share from their self then the original group share should still exist.
59
+     *
60
+     * @param \OCP\Share\IShare $share
61
+     * @param string $recipient UserId of the recipient
62
+     * @since 9.0.0
63
+     */
64
+    public function deleteFromSelf(\OCP\Share\IShare $share, $recipient);
65
+
66
+    /**
67
+     * Restore a share for a given recipient. The implementation could be provider independant.
68
+     *
69
+     * @param IShare $share
70
+     * @param string $recipient
71
+     * @return IShare The restored share object
72
+     *
73
+     * @since 14.0.0
74
+     * @throws GenericShareException In case the share could not be restored
75
+     */
76
+    public function restore(IShare $share, string $recipient): IShare;
77
+
78
+    /**
79
+     * Move a share as a recipient.
80
+     * This is updating the share target. Thus the mount point of the recipient.
81
+     * This may require special handling. If a user moves a group share
82
+     * the target should only be changed for them.
83
+     *
84
+     * @param \OCP\Share\IShare $share
85
+     * @param string $recipient userId of recipient
86
+     * @return \OCP\Share\IShare
87
+     * @since 9.0.0
88
+     */
89
+    public function move(\OCP\Share\IShare $share, $recipient);
90
+
91
+    /**
92
+     * Get all shares by the given user in a folder
93
+     *
94
+     * @param string $userId
95
+     * @param Folder $node
96
+     * @param bool $reshares Also get the shares where $user is the owner instead of just the shares where $user is the initiator
97
+     * @param bool $shallow Whether the method should stop at the first level, or look into sub-folders.
98
+     * @return array<int, list<\OCP\Share\IShare>>
99
+     * @since 11.0.0
100
+     */
101
+    public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true);
102
+
103
+    /**
104
+     * Get all shares by the given user
105
+     *
106
+     * @param string $userId
107
+     * @param int $shareType
108
+     * @param Node|null $node
109
+     * @param bool $reshares Also get the shares where $user is the owner instead of just the shares where $user is the initiator
110
+     * @param int $limit The maximum number of shares to be returned, -1 for all shares
111
+     * @param int $offset
112
+     * @return \OCP\Share\IShare[]
113
+     * @since 9.0.0
114
+     */
115
+    public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset);
116
+
117
+    /**
118
+     * Get share by id
119
+     *
120
+     * @param string $id
121
+     * @param string|null $recipientId
122
+     * @return \OCP\Share\IShare
123
+     * @throws ShareNotFound
124
+     * @since 9.0.0
125
+     */
126
+    public function getShareById($id, $recipientId = null);
127
+
128
+    /**
129
+     * Get shares for a given path
130
+     *
131
+     * @param Node $path
132
+     * @return \OCP\Share\IShare[]
133
+     * @since 9.0.0
134
+     */
135
+    public function getSharesByPath(Node $path);
136
+
137
+    /**
138
+     * Get shared with the given user
139
+     *
140
+     * @param string $userId get shares where this user is the recipient
141
+     * @param int $shareType
142
+     * @param Node|null $node
143
+     * @param int $limit The max number of entries returned, -1 for all
144
+     * @param int $offset
145
+     * @return \OCP\Share\IShare[]
146
+     * @since 9.0.0
147
+     */
148
+    public function getSharedWith($userId, $shareType, $node, $limit, $offset);
149
+
150
+    /**
151
+     * Get a share by token
152
+     *
153
+     * @param string $token
154
+     * @return \OCP\Share\IShare
155
+     * @throws ShareNotFound
156
+     * @since 9.0.0
157
+     */
158
+    public function getShareByToken(string $token);
159
+
160
+    /**
161
+     * A user is deleted from the system
162
+     * So clean up the relevant shares.
163
+     *
164
+     * @param string $uid
165
+     * @param int $shareType
166
+     * @since 9.1.0
167
+     */
168
+    public function userDeleted($uid, $shareType);
169
+
170
+    /**
171
+     * A group is deleted from the system.
172
+     * We have to clean up all shares to this group.
173
+     * Providers not handling group shares should just return
174
+     *
175
+     * @param string $gid
176
+     * @since 9.1.0
177
+     */
178
+    public function groupDeleted($gid);
179
+
180
+    /**
181
+     * A user is deleted from a group
182
+     * We have to clean up all the related user specific group shares
183
+     * Providers not handling group shares should just return
184
+     *
185
+     * @param string $uid
186
+     * @param string $gid
187
+     * @since 9.1.0
188
+     */
189
+    public function userDeletedFromGroup($uid, $gid);
190
+
191
+    /**
192
+     * Get the access list to the array of provided nodes.
193
+     *
194
+     * @see IManager::getAccessList() for sample docs
195
+     *
196
+     * @param Node[] $nodes The list of nodes to get access for
197
+     * @param bool $currentAccess If current access is required (like for removed shares that might get revived later)
198
+     * @return array
199
+     * @since 12
200
+     */
201
+    public function getAccessList($nodes, $currentAccess);
202
+
203
+    /**
204
+     * Get all the shares in this provider returned as iterable to reduce memory
205
+     * overhead
206
+     *
207
+     * @return iterable<IShare>
208
+     * @since 18.0.0
209
+     */
210
+    public function getAllShares(): iterable;
211
+
212
+    /**
213
+     * Get all children of this share
214
+     *
215
+     * @return IShare[]
216
+     * @since 9.0.0
217
+     */
218
+    public function getChildren(IShare $parent);
219 219
 }
Please login to merge, or discard this patch.
lib/public/Federation/ICloudFederationProvider.php 1 patch
Indentation   +43 added lines, -43 removed lines patch added patch discarded remove patch
@@ -22,50 +22,50 @@
 block discarded – undo
22 22
  */
23 23
 
24 24
 interface ICloudFederationProvider {
25
-	/**
26
-	 * get the name of the share type, handled by this provider
27
-	 *
28
-	 * @return string
29
-	 *
30
-	 * @since 14.0.0
31
-	 */
32
-	public function getShareType();
25
+    /**
26
+     * get the name of the share type, handled by this provider
27
+     *
28
+     * @return string
29
+     *
30
+     * @since 14.0.0
31
+     */
32
+    public function getShareType();
33 33
 
34
-	/**
35
-	 * share received from another server
36
-	 *
37
-	 * @param ICloudFederationShare $share
38
-	 * @return string provider specific unique ID of the share
39
-	 *
40
-	 * @throws ProviderCouldNotAddShareException
41
-	 *
42
-	 * @since 14.0.0
43
-	 */
44
-	public function shareReceived(ICloudFederationShare $share);
34
+    /**
35
+     * share received from another server
36
+     *
37
+     * @param ICloudFederationShare $share
38
+     * @return string provider specific unique ID of the share
39
+     *
40
+     * @throws ProviderCouldNotAddShareException
41
+     *
42
+     * @since 14.0.0
43
+     */
44
+    public function shareReceived(ICloudFederationShare $share);
45 45
 
46
-	/**
47
-	 * notification received from another server
48
-	 *
49
-	 * @param string $notificationType (e.g SHARE_ACCEPTED)
50
-	 * @param string $providerId share ID
51
-	 * @param array $notification provider specific notification
52
-	 * @return array<string> $data send back to sender
53
-	 *
54
-	 * @throws ShareNotFound
55
-	 * @throws ActionNotSupportedException
56
-	 * @throws BadRequestException
57
-	 * @throws AuthenticationFailedException
58
-	 *
59
-	 * @since 14.0.0
60
-	 */
61
-	public function notificationReceived(string $notificationType, string $providerId, array $notification);
46
+    /**
47
+     * notification received from another server
48
+     *
49
+     * @param string $notificationType (e.g SHARE_ACCEPTED)
50
+     * @param string $providerId share ID
51
+     * @param array $notification provider specific notification
52
+     * @return array<string> $data send back to sender
53
+     *
54
+     * @throws ShareNotFound
55
+     * @throws ActionNotSupportedException
56
+     * @throws BadRequestException
57
+     * @throws AuthenticationFailedException
58
+     *
59
+     * @since 14.0.0
60
+     */
61
+    public function notificationReceived(string $notificationType, string $providerId, array $notification);
62 62
 
63
-	/**
64
-	 * get the supported share types, e.g. "user", "group", etc.
65
-	 *
66
-	 * @return array
67
-	 *
68
-	 * @since 14.0.0
69
-	 */
70
-	public function getSupportedShareTypes();
63
+    /**
64
+     * get the supported share types, e.g. "user", "group", etc.
65
+     *
66
+     * @return array
67
+     *
68
+     * @since 14.0.0
69
+     */
70
+    public function getSupportedShareTypes();
71 71
 }
Please login to merge, or discard this patch.
lib/public/Activity/IEvent.php 2 patches
Indentation   +355 added lines, -355 removed lines patch added patch discarded remove patch
@@ -20,359 +20,359 @@
 block discarded – undo
20 20
  * @since 8.2.0
21 21
  */
22 22
 interface IEvent {
23
-	/**
24
-	 * Set the app of the activity
25
-	 *
26
-	 * @param string $app
27
-	 * @return IEvent
28
-	 * @throws InvalidValueException if the app id is invalid
29
-	 * @since 8.2.0
30
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
31
-	 */
32
-	public function setApp(string $app): self;
33
-
34
-	/**
35
-	 * Set the type of the activity
36
-	 *
37
-	 * @param string $type
38
-	 * @return IEvent
39
-	 * @throws InvalidValueException if the type is invalid
40
-	 * @since 8.2.0
41
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
42
-	 */
43
-	public function setType(string $type): self;
44
-
45
-	/**
46
-	 * Set the affected user of the activity
47
-	 *
48
-	 * @param string $affectedUser
49
-	 * @return IEvent
50
-	 * @throws InvalidValueException if the affected user is invalid
51
-	 * @since 8.2.0
52
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
53
-	 */
54
-	public function setAffectedUser(string $affectedUser): self;
55
-
56
-	/**
57
-	 * Set the author of the activity
58
-	 *
59
-	 * @param string $author
60
-	 * @return IEvent
61
-	 * @throws InvalidValueException if the author is invalid
62
-	 * @since 8.2.0
63
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
64
-	 */
65
-	public function setAuthor(string $author): self;
66
-
67
-	/**
68
-	 * Set the author of the activity
69
-	 *
70
-	 * @param int $timestamp
71
-	 * @return IEvent
72
-	 * @throws InvalidValueException if the timestamp is invalid
73
-	 * @since 8.2.0
74
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
75
-	 */
76
-	public function setTimestamp(int $timestamp): self;
77
-
78
-	/**
79
-	 * Set the subject of the activity
80
-	 *
81
-	 * @param string $subject
82
-	 * @param array $parameters
83
-	 * @return IEvent
84
-	 * @throws InvalidValueException if the subject or parameters are invalid
85
-	 * @since 8.2.0
86
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
87
-	 */
88
-	public function setSubject(string $subject, array $parameters = []): self;
89
-
90
-	/**
91
-	 * Set a parsed subject
92
-	 *
93
-	 * HTML is not allowed in the parsed subject and will be escaped
94
-	 * automatically by the clients. You can use the RichObjectString system
95
-	 * provided by the Nextcloud server to highlight important parameters via
96
-	 * the setRichSubject method.
97
-	 *
98
-	 * See https://github.com/nextcloud/server/issues/1706 for more information.
99
-	 *
100
-	 * @param string $subject
101
-	 * @return $this
102
-	 * @throws InvalidValueException if the subject is invalid
103
-	 * @since 11.0.0
104
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
105
-	 */
106
-	public function setParsedSubject(string $subject): self;
107
-
108
-	/**
109
-	 * @return string
110
-	 * @since 11.0.0
111
-	 */
112
-	public function getParsedSubject(): string;
113
-
114
-	/**
115
-	 * Set a RichObjectString subject
116
-	 *
117
-	 * HTML is not allowed in the rich subject and will be escaped automatically
118
-	 * by the clients, but you can use the RichObjectString system provided by
119
-	 * the Nextcloud server to highlight important parameters.
120
-	 *
121
-	 * See https://github.com/nextcloud/server/issues/1706 for more information.
122
-	 *
123
-	 * @param string $subject
124
-	 * @param array<string, array<string, string>> $parameters
125
-	 * @return $this
126
-	 * @throws InvalidValueException if the subject or parameters are invalid
127
-	 * @since 11.0.0
128
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
129
-	 */
130
-	public function setRichSubject(string $subject, array $parameters = []): self;
131
-
132
-	/**
133
-	 * @return string
134
-	 * @since 11.0.0
135
-	 */
136
-	public function getRichSubject(): string;
137
-
138
-	/**
139
-	 * @return array<string, array<string, string>>
140
-	 * @since 11.0.0
141
-	 */
142
-	public function getRichSubjectParameters(): array;
143
-
144
-	/**
145
-	 * Set the message of the activity
146
-	 *
147
-	 * @param string $message
148
-	 * @param array $parameters
149
-	 * @return IEvent
150
-	 * @throws InvalidValueException if the message or parameters are invalid
151
-	 * @since 8.2.0
152
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
153
-	 */
154
-	public function setMessage(string $message, array $parameters = []): self;
155
-
156
-	/**
157
-	 * Set a parsed message
158
-	 *
159
-	 * HTML is not allowed in the parsed message and will be escaped
160
-	 * automatically by the clients. You can use the RichObjectString system
161
-	 * provided by the Nextcloud server to highlight important parameters via
162
-	 * the setRichMessage method.
163
-	 *
164
-	 * See https://github.com/nextcloud/server/issues/1706 for more information.
165
-	 *
166
-	 * @param string $message
167
-	 * @return $this
168
-	 * @throws InvalidValueException if the message is invalid
169
-	 * @since 11.0.0
170
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
171
-	 */
172
-	public function setParsedMessage(string $message): self;
173
-
174
-	/**
175
-	 * @return string
176
-	 * @since 11.0.0
177
-	 */
178
-	public function getParsedMessage(): string;
179
-
180
-	/**
181
-	 * Set a RichObjectString message
182
-	 *
183
-	 * HTML is not allowed in the rich message and will be escaped automatically
184
-	 * by the clients, but you can use the RichObjectString system provided by
185
-	 * the Nextcloud server to highlight important parameters.
186
-	 *
187
-	 * See https://github.com/nextcloud/server/issues/1706 for more information.
188
-	 *
189
-	 * @param string $message
190
-	 * @param array<string, array<string, string>> $parameters
191
-	 * @return $this
192
-	 * @throws \InvalidArgumentException if the message or parameters are invalid
193
-	 * @since 11.0.0
194
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
195
-	 */
196
-	public function setRichMessage(string $message, array $parameters = []): self;
197
-
198
-	/**
199
-	 * @return string
200
-	 * @since 11.0.0
201
-	 */
202
-	public function getRichMessage(): string;
203
-
204
-	/**
205
-	 * @return array<string, array<string, string>>
206
-	 * @since 11.0.0
207
-	 */
208
-	public function getRichMessageParameters(): array;
209
-
210
-	/**
211
-	 * Set the object of the activity
212
-	 *
213
-	 * @param string $objectType
214
-	 * @param string|int $objectId
215
-	 * @param string $objectName
216
-	 * @return IEvent
217
-	 * @throws InvalidValueException if the object is invalid
218
-	 * @since 8.2.0
219
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
220
-	 */
221
-	public function setObject(string $objectType, string|int $objectId, string $objectName = ''): self;
222
-
223
-	/**
224
-	 * Set the link of the activity
225
-	 *
226
-	 * @param string $link
227
-	 * @return IEvent
228
-	 * @throws InvalidValueException if the link is invalid
229
-	 * @since 8.2.0
230
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
231
-	 */
232
-	public function setLink(string $link): self;
233
-
234
-	/**
235
-	 * @return string
236
-	 * @since 8.2.0
237
-	 */
238
-	public function getApp(): string;
239
-
240
-	/**
241
-	 * @return string
242
-	 * @since 8.2.0
243
-	 */
244
-	public function getType(): string;
245
-
246
-	/**
247
-	 * @return string
248
-	 * @since 8.2.0
249
-	 */
250
-	public function getAffectedUser(): string;
251
-
252
-	/**
253
-	 * @return string
254
-	 * @since 8.2.0
255
-	 */
256
-	public function getAuthor(): string;
257
-
258
-	/**
259
-	 * @return int
260
-	 * @since 8.2.0
261
-	 */
262
-	public function getTimestamp(): int;
263
-
264
-	/**
265
-	 * @return string
266
-	 * @since 8.2.0
267
-	 */
268
-	public function getSubject(): string;
269
-
270
-	/**
271
-	 * @return array
272
-	 * @since 8.2.0
273
-	 */
274
-	public function getSubjectParameters(): array;
275
-
276
-	/**
277
-	 * @return string
278
-	 * @since 8.2.0
279
-	 */
280
-	public function getMessage(): string;
281
-
282
-	/**
283
-	 * @return array
284
-	 * @since 8.2.0
285
-	 */
286
-	public function getMessageParameters(): array;
287
-
288
-	/**
289
-	 * @return string
290
-	 * @since 8.2.0
291
-	 */
292
-	public function getObjectType(): string;
293
-
294
-	/**
295
-	 * @return string|int
296
-	 * @since 8.2.0
297
-	 */
298
-	public function getObjectId(): string|int;
299
-
300
-	/**
301
-	 * @return string
302
-	 * @since 8.2.0
303
-	 */
304
-	public function getObjectName(): string;
305
-
306
-	/**
307
-	 * @return string
308
-	 * @since 8.2.0
309
-	 */
310
-	public function getLink(): string;
311
-
312
-	/**
313
-	 * Set the absolute url for the icon (should be colored black or not have a color)
314
-	 *
315
-	 * It's automatically color inverted by clients when needed
316
-	 *
317
-	 * @param string $icon
318
-	 * @return $this
319
-	 * @throws InvalidValueException if the icon is invalid
320
-	 * @since 11.0.0
321
-	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
322
-	 */
323
-	public function setIcon(string $icon): self;
324
-
325
-	/**
326
-	 * Get the absolute url for the icon (should be colored black or not have a color)
327
-	 *
328
-	 * It's automatically color inverted by clients when needed
329
-	 *
330
-	 * @return string
331
-	 * @since 11.0.0
332
-	 */
333
-	public function getIcon(): string;
334
-
335
-	/**
336
-	 * @param IEvent $child
337
-	 * @return $this
338
-	 * @since 11.0.0 - Since 15.0.0 returns $this
339
-	 */
340
-	public function setChildEvent(IEvent $child): self;
341
-
342
-	/**
343
-	 * @return IEvent|null
344
-	 * @since 11.0.0
345
-	 */
346
-	public function getChildEvent();
347
-
348
-	/**
349
-	 * @return bool
350
-	 * @since 11.0.0
351
-	 */
352
-	public function isValid(): bool;
353
-
354
-	/**
355
-	 * @return bool
356
-	 * @since 11.0.0
357
-	 */
358
-	public function isValidParsed(): bool;
359
-
360
-	/**
361
-	 * Set whether a notification should be automatically generated for this activity.
362
-	 *
363
-	 * Set this to `false` if the app already generates a notification for the event.
364
-	 *
365
-	 * @param bool $generate
366
-	 * @return IEvent
367
-	 * @since 20.0.0
368
-	 */
369
-	public function setGenerateNotification(bool $generate): self;
370
-
371
-	/**
372
-	 * Whether a notification should be automatically generated for this activity.
373
-	 *
374
-	 * @return bool
375
-	 * @since 20.0.0
376
-	 */
377
-	public function getGenerateNotification(): bool;
23
+    /**
24
+     * Set the app of the activity
25
+     *
26
+     * @param string $app
27
+     * @return IEvent
28
+     * @throws InvalidValueException if the app id is invalid
29
+     * @since 8.2.0
30
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
31
+     */
32
+    public function setApp(string $app): self;
33
+
34
+    /**
35
+     * Set the type of the activity
36
+     *
37
+     * @param string $type
38
+     * @return IEvent
39
+     * @throws InvalidValueException if the type is invalid
40
+     * @since 8.2.0
41
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
42
+     */
43
+    public function setType(string $type): self;
44
+
45
+    /**
46
+     * Set the affected user of the activity
47
+     *
48
+     * @param string $affectedUser
49
+     * @return IEvent
50
+     * @throws InvalidValueException if the affected user is invalid
51
+     * @since 8.2.0
52
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
53
+     */
54
+    public function setAffectedUser(string $affectedUser): self;
55
+
56
+    /**
57
+     * Set the author of the activity
58
+     *
59
+     * @param string $author
60
+     * @return IEvent
61
+     * @throws InvalidValueException if the author is invalid
62
+     * @since 8.2.0
63
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
64
+     */
65
+    public function setAuthor(string $author): self;
66
+
67
+    /**
68
+     * Set the author of the activity
69
+     *
70
+     * @param int $timestamp
71
+     * @return IEvent
72
+     * @throws InvalidValueException if the timestamp is invalid
73
+     * @since 8.2.0
74
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
75
+     */
76
+    public function setTimestamp(int $timestamp): self;
77
+
78
+    /**
79
+     * Set the subject of the activity
80
+     *
81
+     * @param string $subject
82
+     * @param array $parameters
83
+     * @return IEvent
84
+     * @throws InvalidValueException if the subject or parameters are invalid
85
+     * @since 8.2.0
86
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
87
+     */
88
+    public function setSubject(string $subject, array $parameters = []): self;
89
+
90
+    /**
91
+     * Set a parsed subject
92
+     *
93
+     * HTML is not allowed in the parsed subject and will be escaped
94
+     * automatically by the clients. You can use the RichObjectString system
95
+     * provided by the Nextcloud server to highlight important parameters via
96
+     * the setRichSubject method.
97
+     *
98
+     * See https://github.com/nextcloud/server/issues/1706 for more information.
99
+     *
100
+     * @param string $subject
101
+     * @return $this
102
+     * @throws InvalidValueException if the subject is invalid
103
+     * @since 11.0.0
104
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
105
+     */
106
+    public function setParsedSubject(string $subject): self;
107
+
108
+    /**
109
+     * @return string
110
+     * @since 11.0.0
111
+     */
112
+    public function getParsedSubject(): string;
113
+
114
+    /**
115
+     * Set a RichObjectString subject
116
+     *
117
+     * HTML is not allowed in the rich subject and will be escaped automatically
118
+     * by the clients, but you can use the RichObjectString system provided by
119
+     * the Nextcloud server to highlight important parameters.
120
+     *
121
+     * See https://github.com/nextcloud/server/issues/1706 for more information.
122
+     *
123
+     * @param string $subject
124
+     * @param array<string, array<string, string>> $parameters
125
+     * @return $this
126
+     * @throws InvalidValueException if the subject or parameters are invalid
127
+     * @since 11.0.0
128
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
129
+     */
130
+    public function setRichSubject(string $subject, array $parameters = []): self;
131
+
132
+    /**
133
+     * @return string
134
+     * @since 11.0.0
135
+     */
136
+    public function getRichSubject(): string;
137
+
138
+    /**
139
+     * @return array<string, array<string, string>>
140
+     * @since 11.0.0
141
+     */
142
+    public function getRichSubjectParameters(): array;
143
+
144
+    /**
145
+     * Set the message of the activity
146
+     *
147
+     * @param string $message
148
+     * @param array $parameters
149
+     * @return IEvent
150
+     * @throws InvalidValueException if the message or parameters are invalid
151
+     * @since 8.2.0
152
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
153
+     */
154
+    public function setMessage(string $message, array $parameters = []): self;
155
+
156
+    /**
157
+     * Set a parsed message
158
+     *
159
+     * HTML is not allowed in the parsed message and will be escaped
160
+     * automatically by the clients. You can use the RichObjectString system
161
+     * provided by the Nextcloud server to highlight important parameters via
162
+     * the setRichMessage method.
163
+     *
164
+     * See https://github.com/nextcloud/server/issues/1706 for more information.
165
+     *
166
+     * @param string $message
167
+     * @return $this
168
+     * @throws InvalidValueException if the message is invalid
169
+     * @since 11.0.0
170
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
171
+     */
172
+    public function setParsedMessage(string $message): self;
173
+
174
+    /**
175
+     * @return string
176
+     * @since 11.0.0
177
+     */
178
+    public function getParsedMessage(): string;
179
+
180
+    /**
181
+     * Set a RichObjectString message
182
+     *
183
+     * HTML is not allowed in the rich message and will be escaped automatically
184
+     * by the clients, but you can use the RichObjectString system provided by
185
+     * the Nextcloud server to highlight important parameters.
186
+     *
187
+     * See https://github.com/nextcloud/server/issues/1706 for more information.
188
+     *
189
+     * @param string $message
190
+     * @param array<string, array<string, string>> $parameters
191
+     * @return $this
192
+     * @throws \InvalidArgumentException if the message or parameters are invalid
193
+     * @since 11.0.0
194
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
195
+     */
196
+    public function setRichMessage(string $message, array $parameters = []): self;
197
+
198
+    /**
199
+     * @return string
200
+     * @since 11.0.0
201
+     */
202
+    public function getRichMessage(): string;
203
+
204
+    /**
205
+     * @return array<string, array<string, string>>
206
+     * @since 11.0.0
207
+     */
208
+    public function getRichMessageParameters(): array;
209
+
210
+    /**
211
+     * Set the object of the activity
212
+     *
213
+     * @param string $objectType
214
+     * @param string|int $objectId
215
+     * @param string $objectName
216
+     * @return IEvent
217
+     * @throws InvalidValueException if the object is invalid
218
+     * @since 8.2.0
219
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
220
+     */
221
+    public function setObject(string $objectType, string|int $objectId, string $objectName = ''): self;
222
+
223
+    /**
224
+     * Set the link of the activity
225
+     *
226
+     * @param string $link
227
+     * @return IEvent
228
+     * @throws InvalidValueException if the link is invalid
229
+     * @since 8.2.0
230
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
231
+     */
232
+    public function setLink(string $link): self;
233
+
234
+    /**
235
+     * @return string
236
+     * @since 8.2.0
237
+     */
238
+    public function getApp(): string;
239
+
240
+    /**
241
+     * @return string
242
+     * @since 8.2.0
243
+     */
244
+    public function getType(): string;
245
+
246
+    /**
247
+     * @return string
248
+     * @since 8.2.0
249
+     */
250
+    public function getAffectedUser(): string;
251
+
252
+    /**
253
+     * @return string
254
+     * @since 8.2.0
255
+     */
256
+    public function getAuthor(): string;
257
+
258
+    /**
259
+     * @return int
260
+     * @since 8.2.0
261
+     */
262
+    public function getTimestamp(): int;
263
+
264
+    /**
265
+     * @return string
266
+     * @since 8.2.0
267
+     */
268
+    public function getSubject(): string;
269
+
270
+    /**
271
+     * @return array
272
+     * @since 8.2.0
273
+     */
274
+    public function getSubjectParameters(): array;
275
+
276
+    /**
277
+     * @return string
278
+     * @since 8.2.0
279
+     */
280
+    public function getMessage(): string;
281
+
282
+    /**
283
+     * @return array
284
+     * @since 8.2.0
285
+     */
286
+    public function getMessageParameters(): array;
287
+
288
+    /**
289
+     * @return string
290
+     * @since 8.2.0
291
+     */
292
+    public function getObjectType(): string;
293
+
294
+    /**
295
+     * @return string|int
296
+     * @since 8.2.0
297
+     */
298
+    public function getObjectId(): string|int;
299
+
300
+    /**
301
+     * @return string
302
+     * @since 8.2.0
303
+     */
304
+    public function getObjectName(): string;
305
+
306
+    /**
307
+     * @return string
308
+     * @since 8.2.0
309
+     */
310
+    public function getLink(): string;
311
+
312
+    /**
313
+     * Set the absolute url for the icon (should be colored black or not have a color)
314
+     *
315
+     * It's automatically color inverted by clients when needed
316
+     *
317
+     * @param string $icon
318
+     * @return $this
319
+     * @throws InvalidValueException if the icon is invalid
320
+     * @since 11.0.0
321
+     * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
322
+     */
323
+    public function setIcon(string $icon): self;
324
+
325
+    /**
326
+     * Get the absolute url for the icon (should be colored black or not have a color)
327
+     *
328
+     * It's automatically color inverted by clients when needed
329
+     *
330
+     * @return string
331
+     * @since 11.0.0
332
+     */
333
+    public function getIcon(): string;
334
+
335
+    /**
336
+     * @param IEvent $child
337
+     * @return $this
338
+     * @since 11.0.0 - Since 15.0.0 returns $this
339
+     */
340
+    public function setChildEvent(IEvent $child): self;
341
+
342
+    /**
343
+     * @return IEvent|null
344
+     * @since 11.0.0
345
+     */
346
+    public function getChildEvent();
347
+
348
+    /**
349
+     * @return bool
350
+     * @since 11.0.0
351
+     */
352
+    public function isValid(): bool;
353
+
354
+    /**
355
+     * @return bool
356
+     * @since 11.0.0
357
+     */
358
+    public function isValidParsed(): bool;
359
+
360
+    /**
361
+     * Set whether a notification should be automatically generated for this activity.
362
+     *
363
+     * Set this to `false` if the app already generates a notification for the event.
364
+     *
365
+     * @param bool $generate
366
+     * @return IEvent
367
+     * @since 20.0.0
368
+     */
369
+    public function setGenerateNotification(bool $generate): self;
370
+
371
+    /**
372
+     * Whether a notification should be automatically generated for this activity.
373
+     *
374
+     * @return bool
375
+     * @since 20.0.0
376
+     */
377
+    public function getGenerateNotification(): bool;
378 378
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -218,7 +218,7 @@  discard block
 block discarded – undo
218 218
 	 * @since 8.2.0
219 219
 	 * @since 30.0.0 throws {@see InvalidValueException} instead of \InvalidArgumentException
220 220
 	 */
221
-	public function setObject(string $objectType, string|int $objectId, string $objectName = ''): self;
221
+	public function setObject(string $objectType, string | int $objectId, string $objectName = ''): self;
222 222
 
223 223
 	/**
224 224
 	 * Set the link of the activity
@@ -295,7 +295,7 @@  discard block
 block discarded – undo
295 295
 	 * @return string|int
296 296
 	 * @since 8.2.0
297 297
 	 */
298
-	public function getObjectId(): string|int;
298
+	public function getObjectId(): string | int;
299 299
 
300 300
 	/**
301 301
 	 * @return string
Please login to merge, or discard this patch.
lib/private/Files/Node/Folder.php 2 patches
Indentation   +512 added lines, -512 removed lines patch added patch discarded remove patch
@@ -31,516 +31,516 @@
 block discarded – undo
31 31
 
32 32
 class Folder extends Node implements IFolder {
33 33
 
34
-	private ?IUserManager $userManager = null;
35
-
36
-	private bool $wasDeleted = false;
37
-
38
-	/**
39
-	 * Creates a Folder that represents a non-existing path
40
-	 *
41
-	 * @param string $path path
42
-	 * @return NonExistingFolder non-existing node
43
-	 */
44
-	protected function createNonExistingNode($path) {
45
-		return new NonExistingFolder($this->root, $this->view, $path);
46
-	}
47
-
48
-	/**
49
-	 * @param string $path path relative to the folder
50
-	 * @return string
51
-	 * @throws \OCP\Files\NotPermittedException
52
-	 */
53
-	public function getFullPath($path) {
54
-		$path = $this->normalizePath($path);
55
-		if (!$this->isValidPath($path)) {
56
-			throw new NotPermittedException('Invalid path "' . $path . '"');
57
-		}
58
-		return $this->path . $path;
59
-	}
60
-
61
-	/**
62
-	 * @param string $path
63
-	 * @return string|null
64
-	 */
65
-	public function getRelativePath($path) {
66
-		return PathHelper::getRelativePath($this->getPath(), $path);
67
-	}
68
-
69
-	/**
70
-	 * check if a node is a (grand-)child of the folder
71
-	 *
72
-	 * @param \OC\Files\Node\Node $node
73
-	 * @return bool
74
-	 */
75
-	public function isSubNode($node) {
76
-		return str_starts_with($node->getPath(), $this->path . '/');
77
-	}
78
-
79
-	/**
80
-	 * get the content of this directory
81
-	 *
82
-	 * @return Node[]
83
-	 * @throws \OCP\Files\NotFoundException
84
-	 */
85
-	public function getDirectoryListing() {
86
-		$folderContent = $this->view->getDirectoryContent($this->path, '', $this->getFileInfo(false));
87
-
88
-		return array_map(function (FileInfo $info) {
89
-			if ($info->getMimetype() === FileInfo::MIMETYPE_FOLDER) {
90
-				return new Folder($this->root, $this->view, $info->getPath(), $info, $this);
91
-			} else {
92
-				return new File($this->root, $this->view, $info->getPath(), $info, $this);
93
-			}
94
-		}, $folderContent);
95
-	}
96
-
97
-	protected function createNode(string $path, ?FileInfo $info = null, bool $infoHasSubMountsIncluded = true): INode {
98
-		if (is_null($info)) {
99
-			$isDir = $this->view->is_dir($path);
100
-		} else {
101
-			$isDir = $info->getType() === FileInfo::TYPE_FOLDER;
102
-		}
103
-		$parent = dirname($path) === $this->getPath() ? $this : null;
104
-		if ($isDir) {
105
-			return new Folder($this->root, $this->view, $path, $info, $parent, $infoHasSubMountsIncluded);
106
-		} else {
107
-			return new File($this->root, $this->view, $path, $info, $parent);
108
-		}
109
-	}
110
-
111
-	public function get($path) {
112
-		return $this->root->get($this->getFullPath($path));
113
-	}
114
-
115
-	public function nodeExists($path) {
116
-		try {
117
-			$this->get($path);
118
-			return true;
119
-		} catch (NotFoundException|NotPermittedException) {
120
-			return false;
121
-		}
122
-	}
123
-
124
-	/**
125
-	 * @param string $path
126
-	 * @return \OC\Files\Node\Folder
127
-	 * @throws \OCP\Files\NotPermittedException
128
-	 */
129
-	public function newFolder($path) {
130
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
131
-			$fullPath = $this->getFullPath($path);
132
-			$nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath);
133
-			$this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
134
-			if (!$this->view->mkdir($fullPath)) {
135
-				// maybe another concurrent process created the folder already
136
-				if (!$this->view->is_dir($fullPath)) {
137
-					throw new NotPermittedException('Could not create folder "' . $fullPath . '"');
138
-				} else {
139
-					// we need to ensure we don't return before the concurrent request has finished updating the cache
140
-					$tries = 5;
141
-					while (!$this->view->getFileInfo($fullPath)) {
142
-						if ($tries < 1) {
143
-							throw new NotPermittedException('Could not create folder "' . $fullPath . '", folder exists but unable to get cache entry');
144
-						}
145
-						usleep(5 * 1000);
146
-						$tries--;
147
-					}
148
-				}
149
-			}
150
-			$parent = dirname($fullPath) === $this->getPath() ? $this : null;
151
-			$node = new Folder($this->root, $this->view, $fullPath, null, $parent);
152
-			$this->sendHooks(['postWrite', 'postCreate'], [$node]);
153
-			return $node;
154
-		} else {
155
-			throw new NotPermittedException('No create permission for folder "' . $path . '"');
156
-		}
157
-	}
158
-
159
-	/**
160
-	 * @param string $path
161
-	 * @param string | resource | null $content
162
-	 * @return \OC\Files\Node\File
163
-	 * @throws \OCP\Files\NotPermittedException
164
-	 */
165
-	public function newFile($path, $content = null) {
166
-		if ($path === '') {
167
-			throw new NotPermittedException('Could not create as provided path is empty');
168
-		}
169
-		$this->recreateIfNeeded();
170
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
171
-			$fullPath = $this->getFullPath($path);
172
-			$nonExisting = new NonExistingFile($this->root, $this->view, $fullPath);
173
-			$this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
174
-			if ($content !== null) {
175
-				$result = $this->view->file_put_contents($fullPath, $content);
176
-			} else {
177
-				$result = $this->view->touch($fullPath);
178
-			}
179
-			if ($result === false) {
180
-				throw new NotPermittedException('Could not create path "' . $fullPath . '"');
181
-			}
182
-			$node = new File($this->root, $this->view, $fullPath, null, $this);
183
-			$this->sendHooks(['postWrite', 'postCreate'], [$node]);
184
-			return $node;
185
-		}
186
-		throw new NotPermittedException('No create permission for path "' . $path . '"');
187
-	}
188
-
189
-	private function queryFromOperator(ISearchOperator $operator, ?string $uid = null, int $limit = 0, int $offset = 0): ISearchQuery {
190
-		if ($uid === null) {
191
-			$user = null;
192
-		} else {
193
-			/** @var IUserManager $userManager */
194
-			$userManager = \OCP\Server::get(IUserManager::class);
195
-			$user = $userManager->get($uid);
196
-		}
197
-		return new SearchQuery($operator, $limit, $offset, [], $user);
198
-	}
199
-
200
-	/**
201
-	 * search for files with the name matching $query
202
-	 *
203
-	 * @param string|ISearchQuery $query
204
-	 * @return \OC\Files\Node\Node[]
205
-	 */
206
-	public function search($query) {
207
-		if (is_string($query)) {
208
-			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%' . $query . '%'));
209
-		}
210
-
211
-		// search is handled by a single query covering all caches that this folder contains
212
-		// this is done by collect
213
-
214
-		$limitToHome = $query->limitToHome();
215
-		if ($limitToHome && count(explode('/', $this->path)) !== 3) {
216
-			throw new \InvalidArgumentException('searching by owner is only allowed in the users home folder');
217
-		}
218
-
219
-		/** @var QuerySearchHelper $searchHelper */
220
-		$searchHelper = \OC::$server->get(QuerySearchHelper::class);
221
-		[$caches, $mountByMountPoint] = $searchHelper->getCachesAndMountPointsForSearch($this->root, $this->path, $limitToHome);
222
-		$resultsPerCache = $searchHelper->searchInCaches($query, $caches);
223
-
224
-		// loop through all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
225
-		$files = array_merge(...array_map(function (array $results, string $relativeMountPoint) use ($mountByMountPoint) {
226
-			$mount = $mountByMountPoint[$relativeMountPoint];
227
-			return array_map(function (ICacheEntry $result) use ($relativeMountPoint, $mount) {
228
-				return $this->cacheEntryToFileInfo($mount, $relativeMountPoint, $result);
229
-			}, $results);
230
-		}, array_values($resultsPerCache), array_keys($resultsPerCache)));
231
-
232
-		// don't include this folder in the results
233
-		$files = array_values(array_filter($files, function (FileInfo $file) {
234
-			return $file->getPath() !== $this->getPath();
235
-		}));
236
-
237
-		// since results were returned per-cache, they are no longer fully sorted
238
-		$order = $query->getOrder();
239
-		if ($order) {
240
-			usort($files, function (FileInfo $a, FileInfo $b) use ($order) {
241
-				foreach ($order as $orderField) {
242
-					$cmp = $orderField->sortFileInfo($a, $b);
243
-					if ($cmp !== 0) {
244
-						return $cmp;
245
-					}
246
-				}
247
-				return 0;
248
-			});
249
-		}
250
-
251
-		return array_map(function (FileInfo $file) {
252
-			return $this->createNode($file->getPath(), $file);
253
-		}, $files);
254
-	}
255
-
256
-	private function cacheEntryToFileInfo(IMountPoint $mount, string $appendRoot, ICacheEntry $cacheEntry): FileInfo {
257
-		$cacheEntry['internalPath'] = $cacheEntry['path'];
258
-		$cacheEntry['path'] = rtrim($appendRoot . $cacheEntry->getPath(), '/');
259
-		$subPath = $cacheEntry['path'] !== '' ? '/' . $cacheEntry['path'] : '';
260
-		$storage = $mount->getStorage();
261
-
262
-		$owner = null;
263
-		$ownerId = $storage->getOwner($cacheEntry['internalPath']);
264
-		if ($ownerId !== false) {
265
-			// Cache the user manager (for performance)
266
-			if ($this->userManager === null) {
267
-				$this->userManager = \OCP\Server::get(IUserManager::class);
268
-			}
269
-			$owner = new LazyUser($ownerId, $this->userManager);
270
-		}
271
-
272
-		return new \OC\Files\FileInfo(
273
-			$this->path . $subPath,
274
-			$storage,
275
-			$cacheEntry['internalPath'],
276
-			$cacheEntry,
277
-			$mount,
278
-			$owner,
279
-		);
280
-	}
281
-
282
-	/**
283
-	 * search for files by mimetype
284
-	 *
285
-	 * @param string $mimetype
286
-	 * @return Node[]
287
-	 */
288
-	public function searchByMime($mimetype) {
289
-		if (!str_contains($mimetype, '/')) {
290
-			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype . '/%'));
291
-		} else {
292
-			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $mimetype));
293
-		}
294
-		return $this->search($query);
295
-	}
296
-
297
-	/**
298
-	 * search for files by tag
299
-	 *
300
-	 * @param string|int $tag name or tag id
301
-	 * @param string $userId owner of the tags
302
-	 * @return Node[]
303
-	 */
304
-	public function searchByTag($tag, $userId) {
305
-		$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'tagname', $tag), $userId);
306
-		return $this->search($query);
307
-	}
308
-
309
-	public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0): array {
310
-		$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'systemtag', $tagName), $userId, $limit, $offset);
311
-		return $this->search($query);
312
-	}
313
-
314
-	/**
315
-	 * @param int $id
316
-	 * @return \OCP\Files\Node[]
317
-	 */
318
-	public function getById($id) {
319
-		return $this->root->getByIdInPath((int)$id, $this->getPath());
320
-	}
321
-
322
-	public function getFirstNodeById(int $id): ?\OCP\Files\Node {
323
-		return $this->root->getFirstNodeByIdInPath($id, $this->getPath());
324
-	}
325
-
326
-	public function getAppDataDirectoryName(): string {
327
-		$instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid');
328
-		return 'appdata_' . $instanceId;
329
-	}
330
-
331
-	/**
332
-	 * In case the path we are currently in is inside the appdata_* folder,
333
-	 * the original getById method does not work, because it can only look inside
334
-	 * the user's mount points. But the user has no mount point for the root storage.
335
-	 *
336
-	 * So in that case we directly check the mount of the root if it contains
337
-	 * the id. If it does we check if the path is inside the path we are working
338
-	 * in.
339
-	 *
340
-	 * @param int $id
341
-	 * @return array
342
-	 */
343
-	protected function getByIdInRootMount(int $id): array {
344
-		if (!method_exists($this->root, 'createNode')) {
345
-			// Always expected to be false. Being a method of Folder, this is
346
-			// always implemented. For it is an internal method and should not
347
-			// be exposed and made public, it is not part of an interface.
348
-			return [];
349
-		}
350
-		$mount = $this->root->getMount('');
351
-		$storage = $mount->getStorage();
352
-		$cacheEntry = $storage?->getCache($this->path)->get($id);
353
-		if (!$cacheEntry) {
354
-			return [];
355
-		}
356
-
357
-		$absolutePath = '/' . ltrim($cacheEntry->getPath(), '/');
358
-		$currentPath = rtrim($this->path, '/') . '/';
359
-
360
-		if (!str_starts_with($absolutePath, $currentPath)) {
361
-			return [];
362
-		}
363
-
364
-		return [$this->root->createNode(
365
-			$absolutePath, new \OC\Files\FileInfo(
366
-				$absolutePath,
367
-				$storage,
368
-				$cacheEntry->getPath(),
369
-				$cacheEntry,
370
-				$mount
371
-			))];
372
-	}
373
-
374
-	public function getFreeSpace() {
375
-		return $this->view->free_space($this->path);
376
-	}
377
-
378
-	public function delete() {
379
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
380
-			$this->sendHooks(['preDelete']);
381
-			$fileInfo = $this->getFileInfo();
382
-			$this->view->rmdir($this->path);
383
-			$nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
384
-			$this->sendHooks(['postDelete'], [$nonExisting]);
385
-			$this->wasDeleted = true;
386
-		} else {
387
-			throw new NotPermittedException('No delete permission for path "' . $this->path . '"');
388
-		}
389
-	}
390
-
391
-	/**
392
-	 * Add a suffix to the name in case the file exists
393
-	 *
394
-	 * @param string $filename
395
-	 * @return string
396
-	 * @throws NotPermittedException
397
-	 */
398
-	public function getNonExistingName($filename) {
399
-		$path = $this->getPath();
400
-		if ($path === '/') {
401
-			$path = '';
402
-		}
403
-		if ($pos = strrpos($filename, '.')) {
404
-			$name = substr($filename, 0, $pos);
405
-			$ext = substr($filename, $pos);
406
-		} else {
407
-			$name = $filename;
408
-			$ext = '';
409
-		}
410
-
411
-		$newpath = $path . '/' . $filename;
412
-		if ($this->view->file_exists($newpath)) {
413
-			if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
414
-				/** @var array<int<0, max>, array> $matches */
415
-				//Replace the last "(number)" with "(number+1)"
416
-				$last_match = count($matches[0]) - 1;
417
-				$counter = $matches[1][$last_match][0] + 1;
418
-				$offset = $matches[0][$last_match][1];
419
-				$match_length = strlen($matches[0][$last_match][0]);
420
-			} else {
421
-				$counter = 2;
422
-				$match_length = 0;
423
-				$offset = false;
424
-			}
425
-			do {
426
-				if ($offset) {
427
-					//Replace the last "(number)" with "(number+1)"
428
-					$newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
429
-				} else {
430
-					$newname = $name . ' (' . $counter . ')';
431
-				}
432
-				$newpath = $path . '/' . $newname . $ext;
433
-				$counter++;
434
-			} while ($this->view->file_exists($newpath));
435
-		}
436
-
437
-		return trim($this->getRelativePath($newpath), '/');
438
-	}
439
-
440
-	/**
441
-	 * @param int $limit
442
-	 * @param int $offset
443
-	 * @return INode[]
444
-	 */
445
-	public function getRecent($limit, $offset = 0) {
446
-		$filterOutNonEmptyFolder = new SearchBinaryOperator(
447
-			// filter out non empty folders
448
-			ISearchBinaryOperator::OPERATOR_OR,
449
-			[
450
-				new SearchBinaryOperator(
451
-					ISearchBinaryOperator::OPERATOR_NOT,
452
-					[
453
-						new SearchComparison(
454
-							ISearchComparison::COMPARE_EQUAL,
455
-							'mimetype',
456
-							FileInfo::MIMETYPE_FOLDER
457
-						),
458
-					]
459
-				),
460
-				new SearchComparison(
461
-					ISearchComparison::COMPARE_EQUAL,
462
-					'size',
463
-					0
464
-				),
465
-			]
466
-		);
467
-
468
-		$filterNonRecentFiles = new SearchComparison(
469
-			ISearchComparison::COMPARE_GREATER_THAN,
470
-			'mtime',
471
-			strtotime('-2 week')
472
-		);
473
-		if ($offset === 0 && $limit <= 100) {
474
-			$query = new SearchQuery(
475
-				new SearchBinaryOperator(
476
-					ISearchBinaryOperator::OPERATOR_AND,
477
-					[
478
-						$filterOutNonEmptyFolder,
479
-						$filterNonRecentFiles,
480
-					],
481
-				),
482
-				$limit,
483
-				$offset,
484
-				[
485
-					new SearchOrder(
486
-						ISearchOrder::DIRECTION_DESCENDING,
487
-						'mtime'
488
-					),
489
-				]
490
-			);
491
-		} else {
492
-			$query = new SearchQuery(
493
-				$filterOutNonEmptyFolder,
494
-				$limit,
495
-				$offset,
496
-				[
497
-					new SearchOrder(
498
-						ISearchOrder::DIRECTION_DESCENDING,
499
-						'mtime'
500
-					),
501
-				]
502
-			);
503
-		}
504
-
505
-		return $this->search($query);
506
-	}
507
-
508
-	public function verifyPath($fileName, $readonly = false): void {
509
-		$this->view->verifyPath(
510
-			$this->getPath(),
511
-			$fileName,
512
-			$readonly,
513
-		);
514
-	}
515
-
516
-	private function recreateIfNeeded(): void {
517
-		if ($this->wasDeleted) {
518
-			$this->newFolder('');
519
-			$this->wasDeleted = false;
520
-		}
521
-	}
522
-
523
-	#[Override]
524
-	public function getOrCreateFolder(string $path, int $maxRetries = 5): IFolder {
525
-		$i = 0;
526
-		while (true) {
527
-			$path = $i === 0 ? $path : $path . ' (' . $i . ')';
528
-			try {
529
-				$folder = $this->get($path);
530
-				if ($folder instanceof IFolder) {
531
-					return $folder;
532
-				}
533
-			} catch (NotFoundException) {
534
-				$folder = dirname($path) === '.' ? $this : $this->get(dirname($path));
535
-				if (!($folder instanceof Folder)) {
536
-					throw new NotPermittedException("Unable to create folder $path. Parent is not a directory.");
537
-				}
538
-				return $folder->newFolder(basename($path));
539
-			}
540
-			$i++;
541
-			if ($i === $maxRetries) {
542
-				throw new NotPermittedException('Unable to load or create folder.');
543
-			}
544
-		}
545
-	}
34
+    private ?IUserManager $userManager = null;
35
+
36
+    private bool $wasDeleted = false;
37
+
38
+    /**
39
+     * Creates a Folder that represents a non-existing path
40
+     *
41
+     * @param string $path path
42
+     * @return NonExistingFolder non-existing node
43
+     */
44
+    protected function createNonExistingNode($path) {
45
+        return new NonExistingFolder($this->root, $this->view, $path);
46
+    }
47
+
48
+    /**
49
+     * @param string $path path relative to the folder
50
+     * @return string
51
+     * @throws \OCP\Files\NotPermittedException
52
+     */
53
+    public function getFullPath($path) {
54
+        $path = $this->normalizePath($path);
55
+        if (!$this->isValidPath($path)) {
56
+            throw new NotPermittedException('Invalid path "' . $path . '"');
57
+        }
58
+        return $this->path . $path;
59
+    }
60
+
61
+    /**
62
+     * @param string $path
63
+     * @return string|null
64
+     */
65
+    public function getRelativePath($path) {
66
+        return PathHelper::getRelativePath($this->getPath(), $path);
67
+    }
68
+
69
+    /**
70
+     * check if a node is a (grand-)child of the folder
71
+     *
72
+     * @param \OC\Files\Node\Node $node
73
+     * @return bool
74
+     */
75
+    public function isSubNode($node) {
76
+        return str_starts_with($node->getPath(), $this->path . '/');
77
+    }
78
+
79
+    /**
80
+     * get the content of this directory
81
+     *
82
+     * @return Node[]
83
+     * @throws \OCP\Files\NotFoundException
84
+     */
85
+    public function getDirectoryListing() {
86
+        $folderContent = $this->view->getDirectoryContent($this->path, '', $this->getFileInfo(false));
87
+
88
+        return array_map(function (FileInfo $info) {
89
+            if ($info->getMimetype() === FileInfo::MIMETYPE_FOLDER) {
90
+                return new Folder($this->root, $this->view, $info->getPath(), $info, $this);
91
+            } else {
92
+                return new File($this->root, $this->view, $info->getPath(), $info, $this);
93
+            }
94
+        }, $folderContent);
95
+    }
96
+
97
+    protected function createNode(string $path, ?FileInfo $info = null, bool $infoHasSubMountsIncluded = true): INode {
98
+        if (is_null($info)) {
99
+            $isDir = $this->view->is_dir($path);
100
+        } else {
101
+            $isDir = $info->getType() === FileInfo::TYPE_FOLDER;
102
+        }
103
+        $parent = dirname($path) === $this->getPath() ? $this : null;
104
+        if ($isDir) {
105
+            return new Folder($this->root, $this->view, $path, $info, $parent, $infoHasSubMountsIncluded);
106
+        } else {
107
+            return new File($this->root, $this->view, $path, $info, $parent);
108
+        }
109
+    }
110
+
111
+    public function get($path) {
112
+        return $this->root->get($this->getFullPath($path));
113
+    }
114
+
115
+    public function nodeExists($path) {
116
+        try {
117
+            $this->get($path);
118
+            return true;
119
+        } catch (NotFoundException|NotPermittedException) {
120
+            return false;
121
+        }
122
+    }
123
+
124
+    /**
125
+     * @param string $path
126
+     * @return \OC\Files\Node\Folder
127
+     * @throws \OCP\Files\NotPermittedException
128
+     */
129
+    public function newFolder($path) {
130
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
131
+            $fullPath = $this->getFullPath($path);
132
+            $nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath);
133
+            $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
134
+            if (!$this->view->mkdir($fullPath)) {
135
+                // maybe another concurrent process created the folder already
136
+                if (!$this->view->is_dir($fullPath)) {
137
+                    throw new NotPermittedException('Could not create folder "' . $fullPath . '"');
138
+                } else {
139
+                    // we need to ensure we don't return before the concurrent request has finished updating the cache
140
+                    $tries = 5;
141
+                    while (!$this->view->getFileInfo($fullPath)) {
142
+                        if ($tries < 1) {
143
+                            throw new NotPermittedException('Could not create folder "' . $fullPath . '", folder exists but unable to get cache entry');
144
+                        }
145
+                        usleep(5 * 1000);
146
+                        $tries--;
147
+                    }
148
+                }
149
+            }
150
+            $parent = dirname($fullPath) === $this->getPath() ? $this : null;
151
+            $node = new Folder($this->root, $this->view, $fullPath, null, $parent);
152
+            $this->sendHooks(['postWrite', 'postCreate'], [$node]);
153
+            return $node;
154
+        } else {
155
+            throw new NotPermittedException('No create permission for folder "' . $path . '"');
156
+        }
157
+    }
158
+
159
+    /**
160
+     * @param string $path
161
+     * @param string | resource | null $content
162
+     * @return \OC\Files\Node\File
163
+     * @throws \OCP\Files\NotPermittedException
164
+     */
165
+    public function newFile($path, $content = null) {
166
+        if ($path === '') {
167
+            throw new NotPermittedException('Could not create as provided path is empty');
168
+        }
169
+        $this->recreateIfNeeded();
170
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
171
+            $fullPath = $this->getFullPath($path);
172
+            $nonExisting = new NonExistingFile($this->root, $this->view, $fullPath);
173
+            $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
174
+            if ($content !== null) {
175
+                $result = $this->view->file_put_contents($fullPath, $content);
176
+            } else {
177
+                $result = $this->view->touch($fullPath);
178
+            }
179
+            if ($result === false) {
180
+                throw new NotPermittedException('Could not create path "' . $fullPath . '"');
181
+            }
182
+            $node = new File($this->root, $this->view, $fullPath, null, $this);
183
+            $this->sendHooks(['postWrite', 'postCreate'], [$node]);
184
+            return $node;
185
+        }
186
+        throw new NotPermittedException('No create permission for path "' . $path . '"');
187
+    }
188
+
189
+    private function queryFromOperator(ISearchOperator $operator, ?string $uid = null, int $limit = 0, int $offset = 0): ISearchQuery {
190
+        if ($uid === null) {
191
+            $user = null;
192
+        } else {
193
+            /** @var IUserManager $userManager */
194
+            $userManager = \OCP\Server::get(IUserManager::class);
195
+            $user = $userManager->get($uid);
196
+        }
197
+        return new SearchQuery($operator, $limit, $offset, [], $user);
198
+    }
199
+
200
+    /**
201
+     * search for files with the name matching $query
202
+     *
203
+     * @param string|ISearchQuery $query
204
+     * @return \OC\Files\Node\Node[]
205
+     */
206
+    public function search($query) {
207
+        if (is_string($query)) {
208
+            $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%' . $query . '%'));
209
+        }
210
+
211
+        // search is handled by a single query covering all caches that this folder contains
212
+        // this is done by collect
213
+
214
+        $limitToHome = $query->limitToHome();
215
+        if ($limitToHome && count(explode('/', $this->path)) !== 3) {
216
+            throw new \InvalidArgumentException('searching by owner is only allowed in the users home folder');
217
+        }
218
+
219
+        /** @var QuerySearchHelper $searchHelper */
220
+        $searchHelper = \OC::$server->get(QuerySearchHelper::class);
221
+        [$caches, $mountByMountPoint] = $searchHelper->getCachesAndMountPointsForSearch($this->root, $this->path, $limitToHome);
222
+        $resultsPerCache = $searchHelper->searchInCaches($query, $caches);
223
+
224
+        // loop through all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
225
+        $files = array_merge(...array_map(function (array $results, string $relativeMountPoint) use ($mountByMountPoint) {
226
+            $mount = $mountByMountPoint[$relativeMountPoint];
227
+            return array_map(function (ICacheEntry $result) use ($relativeMountPoint, $mount) {
228
+                return $this->cacheEntryToFileInfo($mount, $relativeMountPoint, $result);
229
+            }, $results);
230
+        }, array_values($resultsPerCache), array_keys($resultsPerCache)));
231
+
232
+        // don't include this folder in the results
233
+        $files = array_values(array_filter($files, function (FileInfo $file) {
234
+            return $file->getPath() !== $this->getPath();
235
+        }));
236
+
237
+        // since results were returned per-cache, they are no longer fully sorted
238
+        $order = $query->getOrder();
239
+        if ($order) {
240
+            usort($files, function (FileInfo $a, FileInfo $b) use ($order) {
241
+                foreach ($order as $orderField) {
242
+                    $cmp = $orderField->sortFileInfo($a, $b);
243
+                    if ($cmp !== 0) {
244
+                        return $cmp;
245
+                    }
246
+                }
247
+                return 0;
248
+            });
249
+        }
250
+
251
+        return array_map(function (FileInfo $file) {
252
+            return $this->createNode($file->getPath(), $file);
253
+        }, $files);
254
+    }
255
+
256
+    private function cacheEntryToFileInfo(IMountPoint $mount, string $appendRoot, ICacheEntry $cacheEntry): FileInfo {
257
+        $cacheEntry['internalPath'] = $cacheEntry['path'];
258
+        $cacheEntry['path'] = rtrim($appendRoot . $cacheEntry->getPath(), '/');
259
+        $subPath = $cacheEntry['path'] !== '' ? '/' . $cacheEntry['path'] : '';
260
+        $storage = $mount->getStorage();
261
+
262
+        $owner = null;
263
+        $ownerId = $storage->getOwner($cacheEntry['internalPath']);
264
+        if ($ownerId !== false) {
265
+            // Cache the user manager (for performance)
266
+            if ($this->userManager === null) {
267
+                $this->userManager = \OCP\Server::get(IUserManager::class);
268
+            }
269
+            $owner = new LazyUser($ownerId, $this->userManager);
270
+        }
271
+
272
+        return new \OC\Files\FileInfo(
273
+            $this->path . $subPath,
274
+            $storage,
275
+            $cacheEntry['internalPath'],
276
+            $cacheEntry,
277
+            $mount,
278
+            $owner,
279
+        );
280
+    }
281
+
282
+    /**
283
+     * search for files by mimetype
284
+     *
285
+     * @param string $mimetype
286
+     * @return Node[]
287
+     */
288
+    public function searchByMime($mimetype) {
289
+        if (!str_contains($mimetype, '/')) {
290
+            $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype . '/%'));
291
+        } else {
292
+            $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $mimetype));
293
+        }
294
+        return $this->search($query);
295
+    }
296
+
297
+    /**
298
+     * search for files by tag
299
+     *
300
+     * @param string|int $tag name or tag id
301
+     * @param string $userId owner of the tags
302
+     * @return Node[]
303
+     */
304
+    public function searchByTag($tag, $userId) {
305
+        $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'tagname', $tag), $userId);
306
+        return $this->search($query);
307
+    }
308
+
309
+    public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0): array {
310
+        $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'systemtag', $tagName), $userId, $limit, $offset);
311
+        return $this->search($query);
312
+    }
313
+
314
+    /**
315
+     * @param int $id
316
+     * @return \OCP\Files\Node[]
317
+     */
318
+    public function getById($id) {
319
+        return $this->root->getByIdInPath((int)$id, $this->getPath());
320
+    }
321
+
322
+    public function getFirstNodeById(int $id): ?\OCP\Files\Node {
323
+        return $this->root->getFirstNodeByIdInPath($id, $this->getPath());
324
+    }
325
+
326
+    public function getAppDataDirectoryName(): string {
327
+        $instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid');
328
+        return 'appdata_' . $instanceId;
329
+    }
330
+
331
+    /**
332
+     * In case the path we are currently in is inside the appdata_* folder,
333
+     * the original getById method does not work, because it can only look inside
334
+     * the user's mount points. But the user has no mount point for the root storage.
335
+     *
336
+     * So in that case we directly check the mount of the root if it contains
337
+     * the id. If it does we check if the path is inside the path we are working
338
+     * in.
339
+     *
340
+     * @param int $id
341
+     * @return array
342
+     */
343
+    protected function getByIdInRootMount(int $id): array {
344
+        if (!method_exists($this->root, 'createNode')) {
345
+            // Always expected to be false. Being a method of Folder, this is
346
+            // always implemented. For it is an internal method and should not
347
+            // be exposed and made public, it is not part of an interface.
348
+            return [];
349
+        }
350
+        $mount = $this->root->getMount('');
351
+        $storage = $mount->getStorage();
352
+        $cacheEntry = $storage?->getCache($this->path)->get($id);
353
+        if (!$cacheEntry) {
354
+            return [];
355
+        }
356
+
357
+        $absolutePath = '/' . ltrim($cacheEntry->getPath(), '/');
358
+        $currentPath = rtrim($this->path, '/') . '/';
359
+
360
+        if (!str_starts_with($absolutePath, $currentPath)) {
361
+            return [];
362
+        }
363
+
364
+        return [$this->root->createNode(
365
+            $absolutePath, new \OC\Files\FileInfo(
366
+                $absolutePath,
367
+                $storage,
368
+                $cacheEntry->getPath(),
369
+                $cacheEntry,
370
+                $mount
371
+            ))];
372
+    }
373
+
374
+    public function getFreeSpace() {
375
+        return $this->view->free_space($this->path);
376
+    }
377
+
378
+    public function delete() {
379
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
380
+            $this->sendHooks(['preDelete']);
381
+            $fileInfo = $this->getFileInfo();
382
+            $this->view->rmdir($this->path);
383
+            $nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
384
+            $this->sendHooks(['postDelete'], [$nonExisting]);
385
+            $this->wasDeleted = true;
386
+        } else {
387
+            throw new NotPermittedException('No delete permission for path "' . $this->path . '"');
388
+        }
389
+    }
390
+
391
+    /**
392
+     * Add a suffix to the name in case the file exists
393
+     *
394
+     * @param string $filename
395
+     * @return string
396
+     * @throws NotPermittedException
397
+     */
398
+    public function getNonExistingName($filename) {
399
+        $path = $this->getPath();
400
+        if ($path === '/') {
401
+            $path = '';
402
+        }
403
+        if ($pos = strrpos($filename, '.')) {
404
+            $name = substr($filename, 0, $pos);
405
+            $ext = substr($filename, $pos);
406
+        } else {
407
+            $name = $filename;
408
+            $ext = '';
409
+        }
410
+
411
+        $newpath = $path . '/' . $filename;
412
+        if ($this->view->file_exists($newpath)) {
413
+            if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
414
+                /** @var array<int<0, max>, array> $matches */
415
+                //Replace the last "(number)" with "(number+1)"
416
+                $last_match = count($matches[0]) - 1;
417
+                $counter = $matches[1][$last_match][0] + 1;
418
+                $offset = $matches[0][$last_match][1];
419
+                $match_length = strlen($matches[0][$last_match][0]);
420
+            } else {
421
+                $counter = 2;
422
+                $match_length = 0;
423
+                $offset = false;
424
+            }
425
+            do {
426
+                if ($offset) {
427
+                    //Replace the last "(number)" with "(number+1)"
428
+                    $newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
429
+                } else {
430
+                    $newname = $name . ' (' . $counter . ')';
431
+                }
432
+                $newpath = $path . '/' . $newname . $ext;
433
+                $counter++;
434
+            } while ($this->view->file_exists($newpath));
435
+        }
436
+
437
+        return trim($this->getRelativePath($newpath), '/');
438
+    }
439
+
440
+    /**
441
+     * @param int $limit
442
+     * @param int $offset
443
+     * @return INode[]
444
+     */
445
+    public function getRecent($limit, $offset = 0) {
446
+        $filterOutNonEmptyFolder = new SearchBinaryOperator(
447
+            // filter out non empty folders
448
+            ISearchBinaryOperator::OPERATOR_OR,
449
+            [
450
+                new SearchBinaryOperator(
451
+                    ISearchBinaryOperator::OPERATOR_NOT,
452
+                    [
453
+                        new SearchComparison(
454
+                            ISearchComparison::COMPARE_EQUAL,
455
+                            'mimetype',
456
+                            FileInfo::MIMETYPE_FOLDER
457
+                        ),
458
+                    ]
459
+                ),
460
+                new SearchComparison(
461
+                    ISearchComparison::COMPARE_EQUAL,
462
+                    'size',
463
+                    0
464
+                ),
465
+            ]
466
+        );
467
+
468
+        $filterNonRecentFiles = new SearchComparison(
469
+            ISearchComparison::COMPARE_GREATER_THAN,
470
+            'mtime',
471
+            strtotime('-2 week')
472
+        );
473
+        if ($offset === 0 && $limit <= 100) {
474
+            $query = new SearchQuery(
475
+                new SearchBinaryOperator(
476
+                    ISearchBinaryOperator::OPERATOR_AND,
477
+                    [
478
+                        $filterOutNonEmptyFolder,
479
+                        $filterNonRecentFiles,
480
+                    ],
481
+                ),
482
+                $limit,
483
+                $offset,
484
+                [
485
+                    new SearchOrder(
486
+                        ISearchOrder::DIRECTION_DESCENDING,
487
+                        'mtime'
488
+                    ),
489
+                ]
490
+            );
491
+        } else {
492
+            $query = new SearchQuery(
493
+                $filterOutNonEmptyFolder,
494
+                $limit,
495
+                $offset,
496
+                [
497
+                    new SearchOrder(
498
+                        ISearchOrder::DIRECTION_DESCENDING,
499
+                        'mtime'
500
+                    ),
501
+                ]
502
+            );
503
+        }
504
+
505
+        return $this->search($query);
506
+    }
507
+
508
+    public function verifyPath($fileName, $readonly = false): void {
509
+        $this->view->verifyPath(
510
+            $this->getPath(),
511
+            $fileName,
512
+            $readonly,
513
+        );
514
+    }
515
+
516
+    private function recreateIfNeeded(): void {
517
+        if ($this->wasDeleted) {
518
+            $this->newFolder('');
519
+            $this->wasDeleted = false;
520
+        }
521
+    }
522
+
523
+    #[Override]
524
+    public function getOrCreateFolder(string $path, int $maxRetries = 5): IFolder {
525
+        $i = 0;
526
+        while (true) {
527
+            $path = $i === 0 ? $path : $path . ' (' . $i . ')';
528
+            try {
529
+                $folder = $this->get($path);
530
+                if ($folder instanceof IFolder) {
531
+                    return $folder;
532
+                }
533
+            } catch (NotFoundException) {
534
+                $folder = dirname($path) === '.' ? $this : $this->get(dirname($path));
535
+                if (!($folder instanceof Folder)) {
536
+                    throw new NotPermittedException("Unable to create folder $path. Parent is not a directory.");
537
+                }
538
+                return $folder->newFolder(basename($path));
539
+            }
540
+            $i++;
541
+            if ($i === $maxRetries) {
542
+                throw new NotPermittedException('Unable to load or create folder.');
543
+            }
544
+        }
545
+    }
546 546
 }
Please login to merge, or discard this patch.
Spacing   +30 added lines, -30 removed lines patch added patch discarded remove patch
@@ -53,9 +53,9 @@  discard block
 block discarded – undo
53 53
 	public function getFullPath($path) {
54 54
 		$path = $this->normalizePath($path);
55 55
 		if (!$this->isValidPath($path)) {
56
-			throw new NotPermittedException('Invalid path "' . $path . '"');
56
+			throw new NotPermittedException('Invalid path "'.$path.'"');
57 57
 		}
58
-		return $this->path . $path;
58
+		return $this->path.$path;
59 59
 	}
60 60
 
61 61
 	/**
@@ -73,7 +73,7 @@  discard block
 block discarded – undo
73 73
 	 * @return bool
74 74
 	 */
75 75
 	public function isSubNode($node) {
76
-		return str_starts_with($node->getPath(), $this->path . '/');
76
+		return str_starts_with($node->getPath(), $this->path.'/');
77 77
 	}
78 78
 
79 79
 	/**
@@ -85,7 +85,7 @@  discard block
 block discarded – undo
85 85
 	public function getDirectoryListing() {
86 86
 		$folderContent = $this->view->getDirectoryContent($this->path, '', $this->getFileInfo(false));
87 87
 
88
-		return array_map(function (FileInfo $info) {
88
+		return array_map(function(FileInfo $info) {
89 89
 			if ($info->getMimetype() === FileInfo::MIMETYPE_FOLDER) {
90 90
 				return new Folder($this->root, $this->view, $info->getPath(), $info, $this);
91 91
 			} else {
@@ -116,7 +116,7 @@  discard block
 block discarded – undo
116 116
 		try {
117 117
 			$this->get($path);
118 118
 			return true;
119
-		} catch (NotFoundException|NotPermittedException) {
119
+		} catch (NotFoundException | NotPermittedException) {
120 120
 			return false;
121 121
 		}
122 122
 	}
@@ -134,13 +134,13 @@  discard block
 block discarded – undo
134 134
 			if (!$this->view->mkdir($fullPath)) {
135 135
 				// maybe another concurrent process created the folder already
136 136
 				if (!$this->view->is_dir($fullPath)) {
137
-					throw new NotPermittedException('Could not create folder "' . $fullPath . '"');
137
+					throw new NotPermittedException('Could not create folder "'.$fullPath.'"');
138 138
 				} else {
139 139
 					// we need to ensure we don't return before the concurrent request has finished updating the cache
140 140
 					$tries = 5;
141 141
 					while (!$this->view->getFileInfo($fullPath)) {
142 142
 						if ($tries < 1) {
143
-							throw new NotPermittedException('Could not create folder "' . $fullPath . '", folder exists but unable to get cache entry');
143
+							throw new NotPermittedException('Could not create folder "'.$fullPath.'", folder exists but unable to get cache entry');
144 144
 						}
145 145
 						usleep(5 * 1000);
146 146
 						$tries--;
@@ -152,7 +152,7 @@  discard block
 block discarded – undo
152 152
 			$this->sendHooks(['postWrite', 'postCreate'], [$node]);
153 153
 			return $node;
154 154
 		} else {
155
-			throw new NotPermittedException('No create permission for folder "' . $path . '"');
155
+			throw new NotPermittedException('No create permission for folder "'.$path.'"');
156 156
 		}
157 157
 	}
158 158
 
@@ -177,13 +177,13 @@  discard block
 block discarded – undo
177 177
 				$result = $this->view->touch($fullPath);
178 178
 			}
179 179
 			if ($result === false) {
180
-				throw new NotPermittedException('Could not create path "' . $fullPath . '"');
180
+				throw new NotPermittedException('Could not create path "'.$fullPath.'"');
181 181
 			}
182 182
 			$node = new File($this->root, $this->view, $fullPath, null, $this);
183 183
 			$this->sendHooks(['postWrite', 'postCreate'], [$node]);
184 184
 			return $node;
185 185
 		}
186
-		throw new NotPermittedException('No create permission for path "' . $path . '"');
186
+		throw new NotPermittedException('No create permission for path "'.$path.'"');
187 187
 	}
188 188
 
189 189
 	private function queryFromOperator(ISearchOperator $operator, ?string $uid = null, int $limit = 0, int $offset = 0): ISearchQuery {
@@ -205,7 +205,7 @@  discard block
 block discarded – undo
205 205
 	 */
206 206
 	public function search($query) {
207 207
 		if (is_string($query)) {
208
-			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%' . $query . '%'));
208
+			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%'.$query.'%'));
209 209
 		}
210 210
 
211 211
 		// search is handled by a single query covering all caches that this folder contains
@@ -222,22 +222,22 @@  discard block
 block discarded – undo
222 222
 		$resultsPerCache = $searchHelper->searchInCaches($query, $caches);
223 223
 
224 224
 		// loop through all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
225
-		$files = array_merge(...array_map(function (array $results, string $relativeMountPoint) use ($mountByMountPoint) {
225
+		$files = array_merge(...array_map(function(array $results, string $relativeMountPoint) use ($mountByMountPoint) {
226 226
 			$mount = $mountByMountPoint[$relativeMountPoint];
227
-			return array_map(function (ICacheEntry $result) use ($relativeMountPoint, $mount) {
227
+			return array_map(function(ICacheEntry $result) use ($relativeMountPoint, $mount) {
228 228
 				return $this->cacheEntryToFileInfo($mount, $relativeMountPoint, $result);
229 229
 			}, $results);
230 230
 		}, array_values($resultsPerCache), array_keys($resultsPerCache)));
231 231
 
232 232
 		// don't include this folder in the results
233
-		$files = array_values(array_filter($files, function (FileInfo $file) {
233
+		$files = array_values(array_filter($files, function(FileInfo $file) {
234 234
 			return $file->getPath() !== $this->getPath();
235 235
 		}));
236 236
 
237 237
 		// since results were returned per-cache, they are no longer fully sorted
238 238
 		$order = $query->getOrder();
239 239
 		if ($order) {
240
-			usort($files, function (FileInfo $a, FileInfo $b) use ($order) {
240
+			usort($files, function(FileInfo $a, FileInfo $b) use ($order) {
241 241
 				foreach ($order as $orderField) {
242 242
 					$cmp = $orderField->sortFileInfo($a, $b);
243 243
 					if ($cmp !== 0) {
@@ -248,15 +248,15 @@  discard block
 block discarded – undo
248 248
 			});
249 249
 		}
250 250
 
251
-		return array_map(function (FileInfo $file) {
251
+		return array_map(function(FileInfo $file) {
252 252
 			return $this->createNode($file->getPath(), $file);
253 253
 		}, $files);
254 254
 	}
255 255
 
256 256
 	private function cacheEntryToFileInfo(IMountPoint $mount, string $appendRoot, ICacheEntry $cacheEntry): FileInfo {
257 257
 		$cacheEntry['internalPath'] = $cacheEntry['path'];
258
-		$cacheEntry['path'] = rtrim($appendRoot . $cacheEntry->getPath(), '/');
259
-		$subPath = $cacheEntry['path'] !== '' ? '/' . $cacheEntry['path'] : '';
258
+		$cacheEntry['path'] = rtrim($appendRoot.$cacheEntry->getPath(), '/');
259
+		$subPath = $cacheEntry['path'] !== '' ? '/'.$cacheEntry['path'] : '';
260 260
 		$storage = $mount->getStorage();
261 261
 
262 262
 		$owner = null;
@@ -270,7 +270,7 @@  discard block
 block discarded – undo
270 270
 		}
271 271
 
272 272
 		return new \OC\Files\FileInfo(
273
-			$this->path . $subPath,
273
+			$this->path.$subPath,
274 274
 			$storage,
275 275
 			$cacheEntry['internalPath'],
276 276
 			$cacheEntry,
@@ -287,7 +287,7 @@  discard block
 block discarded – undo
287 287
 	 */
288 288
 	public function searchByMime($mimetype) {
289 289
 		if (!str_contains($mimetype, '/')) {
290
-			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype . '/%'));
290
+			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype.'/%'));
291 291
 		} else {
292 292
 			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $mimetype));
293 293
 		}
@@ -316,7 +316,7 @@  discard block
 block discarded – undo
316 316
 	 * @return \OCP\Files\Node[]
317 317
 	 */
318 318
 	public function getById($id) {
319
-		return $this->root->getByIdInPath((int)$id, $this->getPath());
319
+		return $this->root->getByIdInPath((int) $id, $this->getPath());
320 320
 	}
321 321
 
322 322
 	public function getFirstNodeById(int $id): ?\OCP\Files\Node {
@@ -325,7 +325,7 @@  discard block
 block discarded – undo
325 325
 
326 326
 	public function getAppDataDirectoryName(): string {
327 327
 		$instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid');
328
-		return 'appdata_' . $instanceId;
328
+		return 'appdata_'.$instanceId;
329 329
 	}
330 330
 
331 331
 	/**
@@ -354,8 +354,8 @@  discard block
 block discarded – undo
354 354
 			return [];
355 355
 		}
356 356
 
357
-		$absolutePath = '/' . ltrim($cacheEntry->getPath(), '/');
358
-		$currentPath = rtrim($this->path, '/') . '/';
357
+		$absolutePath = '/'.ltrim($cacheEntry->getPath(), '/');
358
+		$currentPath = rtrim($this->path, '/').'/';
359 359
 
360 360
 		if (!str_starts_with($absolutePath, $currentPath)) {
361 361
 			return [];
@@ -384,7 +384,7 @@  discard block
 block discarded – undo
384 384
 			$this->sendHooks(['postDelete'], [$nonExisting]);
385 385
 			$this->wasDeleted = true;
386 386
 		} else {
387
-			throw new NotPermittedException('No delete permission for path "' . $this->path . '"');
387
+			throw new NotPermittedException('No delete permission for path "'.$this->path.'"');
388 388
 		}
389 389
 	}
390 390
 
@@ -408,7 +408,7 @@  discard block
 block discarded – undo
408 408
 			$ext = '';
409 409
 		}
410 410
 
411
-		$newpath = $path . '/' . $filename;
411
+		$newpath = $path.'/'.$filename;
412 412
 		if ($this->view->file_exists($newpath)) {
413 413
 			if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
414 414
 				/** @var array<int<0, max>, array> $matches */
@@ -425,11 +425,11 @@  discard block
 block discarded – undo
425 425
 			do {
426 426
 				if ($offset) {
427 427
 					//Replace the last "(number)" with "(number+1)"
428
-					$newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
428
+					$newname = substr_replace($name, '('.$counter.')', $offset, $match_length);
429 429
 				} else {
430
-					$newname = $name . ' (' . $counter . ')';
430
+					$newname = $name.' ('.$counter.')';
431 431
 				}
432
-				$newpath = $path . '/' . $newname . $ext;
432
+				$newpath = $path.'/'.$newname.$ext;
433 433
 				$counter++;
434 434
 			} while ($this->view->file_exists($newpath));
435 435
 		}
@@ -524,7 +524,7 @@  discard block
 block discarded – undo
524 524
 	public function getOrCreateFolder(string $path, int $maxRetries = 5): IFolder {
525 525
 		$i = 0;
526 526
 		while (true) {
527
-			$path = $i === 0 ? $path : $path . ' (' . $i . ')';
527
+			$path = $i === 0 ? $path : $path.' ('.$i.')';
528 528
 			try {
529 529
 				$folder = $this->get($path);
530 530
 				if ($folder instanceof IFolder) {
Please login to merge, or discard this patch.
lib/private/Files/Node/LazyFolder.php 1 patch
Indentation   +543 added lines, -543 removed lines patch added patch discarded remove patch
@@ -25,547 +25,547 @@
 block discarded – undo
25 25
  * @package OC\Files\Node
26 26
  */
27 27
 class LazyFolder implements Folder {
28
-	/** @var \Closure(): Folder */
29
-	private \Closure $folderClosure;
30
-	protected ?Folder $folder = null;
31
-	protected IRootFolder $rootFolder;
32
-	protected array $data;
33
-
34
-	/**
35
-	 * @param IRootFolder $rootFolder
36
-	 * @param \Closure(): Folder $folderClosure
37
-	 * @param array $data
38
-	 */
39
-	public function __construct(IRootFolder $rootFolder, \Closure $folderClosure, array $data = []) {
40
-		$this->rootFolder = $rootFolder;
41
-		$this->folderClosure = $folderClosure;
42
-		$this->data = $data;
43
-	}
44
-
45
-	protected function getRootFolder(): IRootFolder {
46
-		return $this->rootFolder;
47
-	}
48
-
49
-	protected function getRealFolder(): Folder {
50
-		if ($this->folder === null) {
51
-			$this->folder = call_user_func($this->folderClosure);
52
-		}
53
-		return $this->folder;
54
-	}
55
-
56
-	/**
57
-	 * Magic method to first get the real rootFolder and then
58
-	 * call $method with $args on it
59
-	 *
60
-	 * @param $method
61
-	 * @param $args
62
-	 * @return mixed
63
-	 */
64
-	public function __call($method, $args) {
65
-		return call_user_func_array([$this->getRealFolder(), $method], $args);
66
-	}
67
-
68
-	/**
69
-	 * @inheritDoc
70
-	 */
71
-	public function getUser() {
72
-		return $this->__call(__FUNCTION__, func_get_args());
73
-	}
74
-
75
-	/**
76
-	 * @inheritDoc
77
-	 */
78
-	public function listen($scope, $method, callable $callback) {
79
-		$this->__call(__FUNCTION__, func_get_args());
80
-	}
81
-
82
-	/**
83
-	 * @inheritDoc
84
-	 */
85
-	public function removeListener($scope = null, $method = null, ?callable $callback = null) {
86
-		$this->__call(__FUNCTION__, func_get_args());
87
-	}
88
-
89
-	/**
90
-	 * @inheritDoc
91
-	 */
92
-	public function emit($scope, $method, $arguments = []) {
93
-		$this->__call(__FUNCTION__, func_get_args());
94
-	}
95
-
96
-	/**
97
-	 * @inheritDoc
98
-	 */
99
-	public function mount($storage, $mountPoint, $arguments = []) {
100
-		$this->__call(__FUNCTION__, func_get_args());
101
-	}
102
-
103
-	/**
104
-	 * @inheritDoc
105
-	 */
106
-	public function getMount(string $mountPoint): IMountPoint {
107
-		return $this->__call(__FUNCTION__, func_get_args());
108
-	}
109
-
110
-	/**
111
-	 * @return IMountPoint[]
112
-	 */
113
-	public function getMountsIn(string $mountPoint): array {
114
-		return $this->__call(__FUNCTION__, func_get_args());
115
-	}
116
-
117
-	/**
118
-	 * @inheritDoc
119
-	 */
120
-	public function getMountByStorageId($storageId) {
121
-		return $this->__call(__FUNCTION__, func_get_args());
122
-	}
123
-
124
-	/**
125
-	 * @inheritDoc
126
-	 */
127
-	public function getMountByNumericStorageId($numericId) {
128
-		return $this->__call(__FUNCTION__, func_get_args());
129
-	}
130
-
131
-	/**
132
-	 * @inheritDoc
133
-	 */
134
-	public function unMount($mount) {
135
-		$this->__call(__FUNCTION__, func_get_args());
136
-	}
137
-
138
-	public function get($path) {
139
-		return $this->getRootFolder()->get($this->getFullPath($path));
140
-	}
141
-
142
-	#[Override]
143
-	public function getOrCreateFolder(string $path, int $maxRetries = 5): Folder {
144
-		return $this->getRootFolder()->getOrCreateFolder($this->getFullPath($path), $maxRetries);
145
-	}
146
-
147
-	/**
148
-	 * @inheritDoc
149
-	 */
150
-	public function rename($targetPath) {
151
-		return $this->__call(__FUNCTION__, func_get_args());
152
-	}
153
-
154
-	/**
155
-	 * @inheritDoc
156
-	 */
157
-	public function delete() {
158
-		return $this->__call(__FUNCTION__, func_get_args());
159
-	}
160
-
161
-	/**
162
-	 * @inheritDoc
163
-	 */
164
-	public function copy($targetPath) {
165
-		return $this->__call(__FUNCTION__, func_get_args());
166
-	}
167
-
168
-	/**
169
-	 * @inheritDoc
170
-	 */
171
-	public function touch($mtime = null) {
172
-		$this->__call(__FUNCTION__, func_get_args());
173
-	}
174
-
175
-	/**
176
-	 * @inheritDoc
177
-	 */
178
-	public function getStorage() {
179
-		return $this->__call(__FUNCTION__, func_get_args());
180
-	}
181
-
182
-	/**
183
-	 * @inheritDoc
184
-	 */
185
-	public function getPath() {
186
-		if (isset($this->data['path'])) {
187
-			return $this->data['path'];
188
-		}
189
-		return $this->__call(__FUNCTION__, func_get_args());
190
-	}
191
-
192
-	/**
193
-	 * @inheritDoc
194
-	 */
195
-	public function getInternalPath() {
196
-		return $this->__call(__FUNCTION__, func_get_args());
197
-	}
198
-
199
-	/**
200
-	 * @inheritDoc
201
-	 */
202
-	public function getId() {
203
-		if (isset($this->data['fileid'])) {
204
-			return $this->data['fileid'];
205
-		}
206
-		return $this->__call(__FUNCTION__, func_get_args());
207
-	}
208
-
209
-	/**
210
-	 * @inheritDoc
211
-	 */
212
-	public function stat() {
213
-		return $this->__call(__FUNCTION__, func_get_args());
214
-	}
215
-
216
-	/**
217
-	 * @inheritDoc
218
-	 */
219
-	public function getMTime() {
220
-		if (isset($this->data['mtime'])) {
221
-			return $this->data['mtime'];
222
-		}
223
-		return $this->__call(__FUNCTION__, func_get_args());
224
-	}
225
-
226
-	/**
227
-	 * @inheritDoc
228
-	 */
229
-	public function getSize($includeMounts = true): int|float {
230
-		if (isset($this->data['size'])) {
231
-			return $this->data['size'];
232
-		}
233
-		return $this->__call(__FUNCTION__, func_get_args());
234
-	}
235
-
236
-	/**
237
-	 * @inheritDoc
238
-	 */
239
-	public function getEtag() {
240
-		if (isset($this->data['etag'])) {
241
-			return $this->data['etag'];
242
-		}
243
-		return $this->__call(__FUNCTION__, func_get_args());
244
-	}
245
-
246
-	/**
247
-	 * @inheritDoc
248
-	 */
249
-	public function getPermissions() {
250
-		if (isset($this->data['permissions'])) {
251
-			return $this->data['permissions'];
252
-		}
253
-		return $this->__call(__FUNCTION__, func_get_args());
254
-	}
255
-
256
-	/**
257
-	 * @inheritDoc
258
-	 */
259
-	public function isReadable() {
260
-		if (isset($this->data['permissions'])) {
261
-			return ($this->data['permissions'] & Constants::PERMISSION_READ) == Constants::PERMISSION_READ;
262
-		}
263
-		return $this->__call(__FUNCTION__, func_get_args());
264
-	}
265
-
266
-	/**
267
-	 * @inheritDoc
268
-	 */
269
-	public function isUpdateable() {
270
-		if (isset($this->data['permissions'])) {
271
-			return ($this->data['permissions'] & Constants::PERMISSION_UPDATE) == Constants::PERMISSION_UPDATE;
272
-		}
273
-		return $this->__call(__FUNCTION__, func_get_args());
274
-	}
275
-
276
-	/**
277
-	 * @inheritDoc
278
-	 */
279
-	public function isDeletable() {
280
-		if (isset($this->data['permissions'])) {
281
-			return ($this->data['permissions'] & Constants::PERMISSION_DELETE) == Constants::PERMISSION_DELETE;
282
-		}
283
-		return $this->__call(__FUNCTION__, func_get_args());
284
-	}
285
-
286
-	/**
287
-	 * @inheritDoc
288
-	 */
289
-	public function isShareable() {
290
-		if (isset($this->data['permissions'])) {
291
-			return ($this->data['permissions'] & Constants::PERMISSION_SHARE) == Constants::PERMISSION_SHARE;
292
-		}
293
-		return $this->__call(__FUNCTION__, func_get_args());
294
-	}
295
-
296
-	/**
297
-	 * @inheritDoc
298
-	 */
299
-	public function getParent() {
300
-		return $this->__call(__FUNCTION__, func_get_args());
301
-	}
302
-
303
-	/**
304
-	 * @inheritDoc
305
-	 */
306
-	public function getName() {
307
-		if (isset($this->data['path'])) {
308
-			return basename($this->data['path']);
309
-		}
310
-		if (isset($this->data['name'])) {
311
-			return $this->data['name'];
312
-		}
313
-		return $this->__call(__FUNCTION__, func_get_args());
314
-	}
315
-
316
-	/**
317
-	 * @inheritDoc
318
-	 */
319
-	public function getUserFolder($userId) {
320
-		return $this->__call(__FUNCTION__, func_get_args());
321
-	}
322
-
323
-	public function getMimetype(): string {
324
-		if (isset($this->data['mimetype'])) {
325
-			return $this->data['mimetype'];
326
-		}
327
-		return $this->__call(__FUNCTION__, func_get_args());
328
-	}
329
-
330
-	/**
331
-	 * @inheritDoc
332
-	 */
333
-	public function getMimePart() {
334
-		if (isset($this->data['mimetype'])) {
335
-			[$part,] = explode('/', $this->data['mimetype']);
336
-			return $part;
337
-		}
338
-		return $this->__call(__FUNCTION__, func_get_args());
339
-	}
340
-
341
-	/**
342
-	 * @inheritDoc
343
-	 */
344
-	public function isEncrypted() {
345
-		return $this->__call(__FUNCTION__, func_get_args());
346
-	}
347
-
348
-	/**
349
-	 * @inheritDoc
350
-	 */
351
-	public function getType() {
352
-		if (isset($this->data['type'])) {
353
-			return $this->data['type'];
354
-		}
355
-		return $this->__call(__FUNCTION__, func_get_args());
356
-	}
357
-
358
-	/**
359
-	 * @inheritDoc
360
-	 */
361
-	public function isShared() {
362
-		return $this->__call(__FUNCTION__, func_get_args());
363
-	}
364
-
365
-	/**
366
-	 * @inheritDoc
367
-	 */
368
-	public function isMounted() {
369
-		return $this->__call(__FUNCTION__, func_get_args());
370
-	}
371
-
372
-	/**
373
-	 * @inheritDoc
374
-	 */
375
-	public function getMountPoint() {
376
-		return $this->__call(__FUNCTION__, func_get_args());
377
-	}
378
-
379
-	/**
380
-	 * @inheritDoc
381
-	 */
382
-	public function getOwner() {
383
-		return $this->__call(__FUNCTION__, func_get_args());
384
-	}
385
-
386
-	/**
387
-	 * @inheritDoc
388
-	 */
389
-	public function getChecksum() {
390
-		return $this->__call(__FUNCTION__, func_get_args());
391
-	}
392
-
393
-	public function getExtension(): string {
394
-		return $this->__call(__FUNCTION__, func_get_args());
395
-	}
396
-
397
-	/**
398
-	 * @inheritDoc
399
-	 */
400
-	public function getFullPath($path) {
401
-		if (isset($this->data['path'])) {
402
-			$path = PathHelper::normalizePath($path);
403
-			if (!Filesystem::isValidPath($path)) {
404
-				throw new NotPermittedException('Invalid path "' . $path . '"');
405
-			}
406
-			return $this->data['path'] . $path;
407
-		}
408
-		return $this->__call(__FUNCTION__, func_get_args());
409
-	}
410
-
411
-	/**
412
-	 * @inheritDoc
413
-	 */
414
-	public function isSubNode($node) {
415
-		return $this->__call(__FUNCTION__, func_get_args());
416
-	}
417
-
418
-	/**
419
-	 * @inheritDoc
420
-	 */
421
-	public function getDirectoryListing() {
422
-		return $this->__call(__FUNCTION__, func_get_args());
423
-	}
424
-
425
-	public function nodeExists($path) {
426
-		return $this->__call(__FUNCTION__, func_get_args());
427
-	}
428
-
429
-	/**
430
-	 * @inheritDoc
431
-	 */
432
-	public function newFolder($path) {
433
-		return $this->__call(__FUNCTION__, func_get_args());
434
-	}
435
-
436
-	/**
437
-	 * @inheritDoc
438
-	 */
439
-	public function newFile($path, $content = null) {
440
-		return $this->__call(__FUNCTION__, func_get_args());
441
-	}
442
-
443
-	/**
444
-	 * @inheritDoc
445
-	 */
446
-	public function search($query) {
447
-		return $this->__call(__FUNCTION__, func_get_args());
448
-	}
449
-
450
-	/**
451
-	 * @inheritDoc
452
-	 */
453
-	public function searchByMime($mimetype) {
454
-		return $this->__call(__FUNCTION__, func_get_args());
455
-	}
456
-
457
-	/**
458
-	 * @inheritDoc
459
-	 */
460
-	public function searchByTag($tag, $userId) {
461
-		return $this->__call(__FUNCTION__, func_get_args());
462
-	}
463
-
464
-	public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0) {
465
-		return $this->__call(__FUNCTION__, func_get_args());
466
-	}
467
-
468
-	/**
469
-	 * @inheritDoc
470
-	 */
471
-	public function getById($id) {
472
-		return $this->getRootFolder()->getByIdInPath((int)$id, $this->getPath());
473
-	}
474
-
475
-	public function getFirstNodeById(int $id): ?\OCP\Files\Node {
476
-		return $this->getRootFolder()->getFirstNodeByIdInPath($id, $this->getPath());
477
-	}
478
-
479
-	/**
480
-	 * @inheritDoc
481
-	 */
482
-	public function getFreeSpace() {
483
-		return $this->__call(__FUNCTION__, func_get_args());
484
-	}
485
-
486
-	/**
487
-	 * @inheritDoc
488
-	 */
489
-	public function isCreatable() {
490
-		return $this->__call(__FUNCTION__, func_get_args());
491
-	}
492
-
493
-	/**
494
-	 * @inheritDoc
495
-	 */
496
-	public function getNonExistingName($filename) {
497
-		return $this->__call(__FUNCTION__, func_get_args());
498
-	}
499
-
500
-	/**
501
-	 * @inheritDoc
502
-	 */
503
-	public function move($targetPath) {
504
-		return $this->__call(__FUNCTION__, func_get_args());
505
-	}
506
-
507
-	/**
508
-	 * @inheritDoc
509
-	 */
510
-	public function lock($type) {
511
-		return $this->__call(__FUNCTION__, func_get_args());
512
-	}
513
-
514
-	/**
515
-	 * @inheritDoc
516
-	 */
517
-	public function changeLock($targetType) {
518
-		return $this->__call(__FUNCTION__, func_get_args());
519
-	}
520
-
521
-	/**
522
-	 * @inheritDoc
523
-	 */
524
-	public function unlock($type) {
525
-		return $this->__call(__FUNCTION__, func_get_args());
526
-	}
527
-
528
-	/**
529
-	 * @inheritDoc
530
-	 */
531
-	public function getRecent($limit, $offset = 0) {
532
-		return $this->__call(__FUNCTION__, func_get_args());
533
-	}
534
-
535
-	/**
536
-	 * @inheritDoc
537
-	 */
538
-	public function getCreationTime(): int {
539
-		return $this->__call(__FUNCTION__, func_get_args());
540
-	}
541
-
542
-	/**
543
-	 * @inheritDoc
544
-	 */
545
-	public function getUploadTime(): int {
546
-		return $this->__call(__FUNCTION__, func_get_args());
547
-	}
548
-
549
-	public function getRelativePath($path) {
550
-		return PathHelper::getRelativePath($this->getPath(), $path);
551
-	}
552
-
553
-	public function getParentId(): int {
554
-		if (isset($this->data['parent'])) {
555
-			return $this->data['parent'];
556
-		}
557
-		return $this->__call(__FUNCTION__, func_get_args());
558
-	}
559
-
560
-	/**
561
-	 * @inheritDoc
562
-	 * @return array<string, int|string|bool|float|string[]|int[]>
563
-	 */
564
-	public function getMetadata(): array {
565
-		return $this->data['metadata'] ?? $this->__call(__FUNCTION__, func_get_args());
566
-	}
567
-
568
-	public function verifyPath($fileName, $readonly = false): void {
569
-		$this->__call(__FUNCTION__, func_get_args());
570
-	}
28
+    /** @var \Closure(): Folder */
29
+    private \Closure $folderClosure;
30
+    protected ?Folder $folder = null;
31
+    protected IRootFolder $rootFolder;
32
+    protected array $data;
33
+
34
+    /**
35
+     * @param IRootFolder $rootFolder
36
+     * @param \Closure(): Folder $folderClosure
37
+     * @param array $data
38
+     */
39
+    public function __construct(IRootFolder $rootFolder, \Closure $folderClosure, array $data = []) {
40
+        $this->rootFolder = $rootFolder;
41
+        $this->folderClosure = $folderClosure;
42
+        $this->data = $data;
43
+    }
44
+
45
+    protected function getRootFolder(): IRootFolder {
46
+        return $this->rootFolder;
47
+    }
48
+
49
+    protected function getRealFolder(): Folder {
50
+        if ($this->folder === null) {
51
+            $this->folder = call_user_func($this->folderClosure);
52
+        }
53
+        return $this->folder;
54
+    }
55
+
56
+    /**
57
+     * Magic method to first get the real rootFolder and then
58
+     * call $method with $args on it
59
+     *
60
+     * @param $method
61
+     * @param $args
62
+     * @return mixed
63
+     */
64
+    public function __call($method, $args) {
65
+        return call_user_func_array([$this->getRealFolder(), $method], $args);
66
+    }
67
+
68
+    /**
69
+     * @inheritDoc
70
+     */
71
+    public function getUser() {
72
+        return $this->__call(__FUNCTION__, func_get_args());
73
+    }
74
+
75
+    /**
76
+     * @inheritDoc
77
+     */
78
+    public function listen($scope, $method, callable $callback) {
79
+        $this->__call(__FUNCTION__, func_get_args());
80
+    }
81
+
82
+    /**
83
+     * @inheritDoc
84
+     */
85
+    public function removeListener($scope = null, $method = null, ?callable $callback = null) {
86
+        $this->__call(__FUNCTION__, func_get_args());
87
+    }
88
+
89
+    /**
90
+     * @inheritDoc
91
+     */
92
+    public function emit($scope, $method, $arguments = []) {
93
+        $this->__call(__FUNCTION__, func_get_args());
94
+    }
95
+
96
+    /**
97
+     * @inheritDoc
98
+     */
99
+    public function mount($storage, $mountPoint, $arguments = []) {
100
+        $this->__call(__FUNCTION__, func_get_args());
101
+    }
102
+
103
+    /**
104
+     * @inheritDoc
105
+     */
106
+    public function getMount(string $mountPoint): IMountPoint {
107
+        return $this->__call(__FUNCTION__, func_get_args());
108
+    }
109
+
110
+    /**
111
+     * @return IMountPoint[]
112
+     */
113
+    public function getMountsIn(string $mountPoint): array {
114
+        return $this->__call(__FUNCTION__, func_get_args());
115
+    }
116
+
117
+    /**
118
+     * @inheritDoc
119
+     */
120
+    public function getMountByStorageId($storageId) {
121
+        return $this->__call(__FUNCTION__, func_get_args());
122
+    }
123
+
124
+    /**
125
+     * @inheritDoc
126
+     */
127
+    public function getMountByNumericStorageId($numericId) {
128
+        return $this->__call(__FUNCTION__, func_get_args());
129
+    }
130
+
131
+    /**
132
+     * @inheritDoc
133
+     */
134
+    public function unMount($mount) {
135
+        $this->__call(__FUNCTION__, func_get_args());
136
+    }
137
+
138
+    public function get($path) {
139
+        return $this->getRootFolder()->get($this->getFullPath($path));
140
+    }
141
+
142
+    #[Override]
143
+    public function getOrCreateFolder(string $path, int $maxRetries = 5): Folder {
144
+        return $this->getRootFolder()->getOrCreateFolder($this->getFullPath($path), $maxRetries);
145
+    }
146
+
147
+    /**
148
+     * @inheritDoc
149
+     */
150
+    public function rename($targetPath) {
151
+        return $this->__call(__FUNCTION__, func_get_args());
152
+    }
153
+
154
+    /**
155
+     * @inheritDoc
156
+     */
157
+    public function delete() {
158
+        return $this->__call(__FUNCTION__, func_get_args());
159
+    }
160
+
161
+    /**
162
+     * @inheritDoc
163
+     */
164
+    public function copy($targetPath) {
165
+        return $this->__call(__FUNCTION__, func_get_args());
166
+    }
167
+
168
+    /**
169
+     * @inheritDoc
170
+     */
171
+    public function touch($mtime = null) {
172
+        $this->__call(__FUNCTION__, func_get_args());
173
+    }
174
+
175
+    /**
176
+     * @inheritDoc
177
+     */
178
+    public function getStorage() {
179
+        return $this->__call(__FUNCTION__, func_get_args());
180
+    }
181
+
182
+    /**
183
+     * @inheritDoc
184
+     */
185
+    public function getPath() {
186
+        if (isset($this->data['path'])) {
187
+            return $this->data['path'];
188
+        }
189
+        return $this->__call(__FUNCTION__, func_get_args());
190
+    }
191
+
192
+    /**
193
+     * @inheritDoc
194
+     */
195
+    public function getInternalPath() {
196
+        return $this->__call(__FUNCTION__, func_get_args());
197
+    }
198
+
199
+    /**
200
+     * @inheritDoc
201
+     */
202
+    public function getId() {
203
+        if (isset($this->data['fileid'])) {
204
+            return $this->data['fileid'];
205
+        }
206
+        return $this->__call(__FUNCTION__, func_get_args());
207
+    }
208
+
209
+    /**
210
+     * @inheritDoc
211
+     */
212
+    public function stat() {
213
+        return $this->__call(__FUNCTION__, func_get_args());
214
+    }
215
+
216
+    /**
217
+     * @inheritDoc
218
+     */
219
+    public function getMTime() {
220
+        if (isset($this->data['mtime'])) {
221
+            return $this->data['mtime'];
222
+        }
223
+        return $this->__call(__FUNCTION__, func_get_args());
224
+    }
225
+
226
+    /**
227
+     * @inheritDoc
228
+     */
229
+    public function getSize($includeMounts = true): int|float {
230
+        if (isset($this->data['size'])) {
231
+            return $this->data['size'];
232
+        }
233
+        return $this->__call(__FUNCTION__, func_get_args());
234
+    }
235
+
236
+    /**
237
+     * @inheritDoc
238
+     */
239
+    public function getEtag() {
240
+        if (isset($this->data['etag'])) {
241
+            return $this->data['etag'];
242
+        }
243
+        return $this->__call(__FUNCTION__, func_get_args());
244
+    }
245
+
246
+    /**
247
+     * @inheritDoc
248
+     */
249
+    public function getPermissions() {
250
+        if (isset($this->data['permissions'])) {
251
+            return $this->data['permissions'];
252
+        }
253
+        return $this->__call(__FUNCTION__, func_get_args());
254
+    }
255
+
256
+    /**
257
+     * @inheritDoc
258
+     */
259
+    public function isReadable() {
260
+        if (isset($this->data['permissions'])) {
261
+            return ($this->data['permissions'] & Constants::PERMISSION_READ) == Constants::PERMISSION_READ;
262
+        }
263
+        return $this->__call(__FUNCTION__, func_get_args());
264
+    }
265
+
266
+    /**
267
+     * @inheritDoc
268
+     */
269
+    public function isUpdateable() {
270
+        if (isset($this->data['permissions'])) {
271
+            return ($this->data['permissions'] & Constants::PERMISSION_UPDATE) == Constants::PERMISSION_UPDATE;
272
+        }
273
+        return $this->__call(__FUNCTION__, func_get_args());
274
+    }
275
+
276
+    /**
277
+     * @inheritDoc
278
+     */
279
+    public function isDeletable() {
280
+        if (isset($this->data['permissions'])) {
281
+            return ($this->data['permissions'] & Constants::PERMISSION_DELETE) == Constants::PERMISSION_DELETE;
282
+        }
283
+        return $this->__call(__FUNCTION__, func_get_args());
284
+    }
285
+
286
+    /**
287
+     * @inheritDoc
288
+     */
289
+    public function isShareable() {
290
+        if (isset($this->data['permissions'])) {
291
+            return ($this->data['permissions'] & Constants::PERMISSION_SHARE) == Constants::PERMISSION_SHARE;
292
+        }
293
+        return $this->__call(__FUNCTION__, func_get_args());
294
+    }
295
+
296
+    /**
297
+     * @inheritDoc
298
+     */
299
+    public function getParent() {
300
+        return $this->__call(__FUNCTION__, func_get_args());
301
+    }
302
+
303
+    /**
304
+     * @inheritDoc
305
+     */
306
+    public function getName() {
307
+        if (isset($this->data['path'])) {
308
+            return basename($this->data['path']);
309
+        }
310
+        if (isset($this->data['name'])) {
311
+            return $this->data['name'];
312
+        }
313
+        return $this->__call(__FUNCTION__, func_get_args());
314
+    }
315
+
316
+    /**
317
+     * @inheritDoc
318
+     */
319
+    public function getUserFolder($userId) {
320
+        return $this->__call(__FUNCTION__, func_get_args());
321
+    }
322
+
323
+    public function getMimetype(): string {
324
+        if (isset($this->data['mimetype'])) {
325
+            return $this->data['mimetype'];
326
+        }
327
+        return $this->__call(__FUNCTION__, func_get_args());
328
+    }
329
+
330
+    /**
331
+     * @inheritDoc
332
+     */
333
+    public function getMimePart() {
334
+        if (isset($this->data['mimetype'])) {
335
+            [$part,] = explode('/', $this->data['mimetype']);
336
+            return $part;
337
+        }
338
+        return $this->__call(__FUNCTION__, func_get_args());
339
+    }
340
+
341
+    /**
342
+     * @inheritDoc
343
+     */
344
+    public function isEncrypted() {
345
+        return $this->__call(__FUNCTION__, func_get_args());
346
+    }
347
+
348
+    /**
349
+     * @inheritDoc
350
+     */
351
+    public function getType() {
352
+        if (isset($this->data['type'])) {
353
+            return $this->data['type'];
354
+        }
355
+        return $this->__call(__FUNCTION__, func_get_args());
356
+    }
357
+
358
+    /**
359
+     * @inheritDoc
360
+     */
361
+    public function isShared() {
362
+        return $this->__call(__FUNCTION__, func_get_args());
363
+    }
364
+
365
+    /**
366
+     * @inheritDoc
367
+     */
368
+    public function isMounted() {
369
+        return $this->__call(__FUNCTION__, func_get_args());
370
+    }
371
+
372
+    /**
373
+     * @inheritDoc
374
+     */
375
+    public function getMountPoint() {
376
+        return $this->__call(__FUNCTION__, func_get_args());
377
+    }
378
+
379
+    /**
380
+     * @inheritDoc
381
+     */
382
+    public function getOwner() {
383
+        return $this->__call(__FUNCTION__, func_get_args());
384
+    }
385
+
386
+    /**
387
+     * @inheritDoc
388
+     */
389
+    public function getChecksum() {
390
+        return $this->__call(__FUNCTION__, func_get_args());
391
+    }
392
+
393
+    public function getExtension(): string {
394
+        return $this->__call(__FUNCTION__, func_get_args());
395
+    }
396
+
397
+    /**
398
+     * @inheritDoc
399
+     */
400
+    public function getFullPath($path) {
401
+        if (isset($this->data['path'])) {
402
+            $path = PathHelper::normalizePath($path);
403
+            if (!Filesystem::isValidPath($path)) {
404
+                throw new NotPermittedException('Invalid path "' . $path . '"');
405
+            }
406
+            return $this->data['path'] . $path;
407
+        }
408
+        return $this->__call(__FUNCTION__, func_get_args());
409
+    }
410
+
411
+    /**
412
+     * @inheritDoc
413
+     */
414
+    public function isSubNode($node) {
415
+        return $this->__call(__FUNCTION__, func_get_args());
416
+    }
417
+
418
+    /**
419
+     * @inheritDoc
420
+     */
421
+    public function getDirectoryListing() {
422
+        return $this->__call(__FUNCTION__, func_get_args());
423
+    }
424
+
425
+    public function nodeExists($path) {
426
+        return $this->__call(__FUNCTION__, func_get_args());
427
+    }
428
+
429
+    /**
430
+     * @inheritDoc
431
+     */
432
+    public function newFolder($path) {
433
+        return $this->__call(__FUNCTION__, func_get_args());
434
+    }
435
+
436
+    /**
437
+     * @inheritDoc
438
+     */
439
+    public function newFile($path, $content = null) {
440
+        return $this->__call(__FUNCTION__, func_get_args());
441
+    }
442
+
443
+    /**
444
+     * @inheritDoc
445
+     */
446
+    public function search($query) {
447
+        return $this->__call(__FUNCTION__, func_get_args());
448
+    }
449
+
450
+    /**
451
+     * @inheritDoc
452
+     */
453
+    public function searchByMime($mimetype) {
454
+        return $this->__call(__FUNCTION__, func_get_args());
455
+    }
456
+
457
+    /**
458
+     * @inheritDoc
459
+     */
460
+    public function searchByTag($tag, $userId) {
461
+        return $this->__call(__FUNCTION__, func_get_args());
462
+    }
463
+
464
+    public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0) {
465
+        return $this->__call(__FUNCTION__, func_get_args());
466
+    }
467
+
468
+    /**
469
+     * @inheritDoc
470
+     */
471
+    public function getById($id) {
472
+        return $this->getRootFolder()->getByIdInPath((int)$id, $this->getPath());
473
+    }
474
+
475
+    public function getFirstNodeById(int $id): ?\OCP\Files\Node {
476
+        return $this->getRootFolder()->getFirstNodeByIdInPath($id, $this->getPath());
477
+    }
478
+
479
+    /**
480
+     * @inheritDoc
481
+     */
482
+    public function getFreeSpace() {
483
+        return $this->__call(__FUNCTION__, func_get_args());
484
+    }
485
+
486
+    /**
487
+     * @inheritDoc
488
+     */
489
+    public function isCreatable() {
490
+        return $this->__call(__FUNCTION__, func_get_args());
491
+    }
492
+
493
+    /**
494
+     * @inheritDoc
495
+     */
496
+    public function getNonExistingName($filename) {
497
+        return $this->__call(__FUNCTION__, func_get_args());
498
+    }
499
+
500
+    /**
501
+     * @inheritDoc
502
+     */
503
+    public function move($targetPath) {
504
+        return $this->__call(__FUNCTION__, func_get_args());
505
+    }
506
+
507
+    /**
508
+     * @inheritDoc
509
+     */
510
+    public function lock($type) {
511
+        return $this->__call(__FUNCTION__, func_get_args());
512
+    }
513
+
514
+    /**
515
+     * @inheritDoc
516
+     */
517
+    public function changeLock($targetType) {
518
+        return $this->__call(__FUNCTION__, func_get_args());
519
+    }
520
+
521
+    /**
522
+     * @inheritDoc
523
+     */
524
+    public function unlock($type) {
525
+        return $this->__call(__FUNCTION__, func_get_args());
526
+    }
527
+
528
+    /**
529
+     * @inheritDoc
530
+     */
531
+    public function getRecent($limit, $offset = 0) {
532
+        return $this->__call(__FUNCTION__, func_get_args());
533
+    }
534
+
535
+    /**
536
+     * @inheritDoc
537
+     */
538
+    public function getCreationTime(): int {
539
+        return $this->__call(__FUNCTION__, func_get_args());
540
+    }
541
+
542
+    /**
543
+     * @inheritDoc
544
+     */
545
+    public function getUploadTime(): int {
546
+        return $this->__call(__FUNCTION__, func_get_args());
547
+    }
548
+
549
+    public function getRelativePath($path) {
550
+        return PathHelper::getRelativePath($this->getPath(), $path);
551
+    }
552
+
553
+    public function getParentId(): int {
554
+        if (isset($this->data['parent'])) {
555
+            return $this->data['parent'];
556
+        }
557
+        return $this->__call(__FUNCTION__, func_get_args());
558
+    }
559
+
560
+    /**
561
+     * @inheritDoc
562
+     * @return array<string, int|string|bool|float|string[]|int[]>
563
+     */
564
+    public function getMetadata(): array {
565
+        return $this->data['metadata'] ?? $this->__call(__FUNCTION__, func_get_args());
566
+    }
567
+
568
+    public function verifyPath($fileName, $readonly = false): void {
569
+        $this->__call(__FUNCTION__, func_get_args());
570
+    }
571 571
 }
Please login to merge, or discard this patch.
lib/private/Share20/DefaultShareProvider.php 2 patches
Indentation   +1597 added lines, -1597 removed lines patch added patch discarded remove patch
@@ -45,1637 +45,1637 @@
 block discarded – undo
45 45
  * @package OC\Share20
46 46
  */
47 47
 class DefaultShareProvider implements IShareProviderWithNotification, IShareProviderSupportsAccept, IShareProviderSupportsAllSharesInFolder {
48
-	public function __construct(
49
-		private IDBConnection $dbConn,
50
-		private IUserManager $userManager,
51
-		private IGroupManager $groupManager,
52
-		private IRootFolder $rootFolder,
53
-		private IMailer $mailer,
54
-		private Defaults $defaults,
55
-		private IFactory $l10nFactory,
56
-		private IURLGenerator $urlGenerator,
57
-		private ITimeFactory $timeFactory,
58
-		private LoggerInterface $logger,
59
-		private IManager $shareManager,
60
-		private IConfig $config,
61
-	) {
62
-	}
63
-
64
-	/**
65
-	 * Return the identifier of this provider.
66
-	 *
67
-	 * @return string Containing only [a-zA-Z0-9]
68
-	 */
69
-	public function identifier() {
70
-		return 'ocinternal';
71
-	}
72
-
73
-	/**
74
-	 * Share a path
75
-	 *
76
-	 * @param \OCP\Share\IShare $share
77
-	 * @return \OCP\Share\IShare The share object
78
-	 * @throws ShareNotFound
79
-	 * @throws \Exception
80
-	 */
81
-	public function create(\OCP\Share\IShare $share) {
82
-		$qb = $this->dbConn->getQueryBuilder();
83
-
84
-		$qb->insert('share');
85
-		$qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
86
-
87
-		$expirationDate = $share->getExpirationDate();
88
-		if ($expirationDate !== null) {
89
-			$expirationDate = clone $expirationDate;
90
-			$expirationDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
91
-		}
92
-
93
-		if ($share->getShareType() === IShare::TYPE_USER) {
94
-			//Set the UID of the user we share with
95
-			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
96
-			$qb->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_PENDING));
97
-
98
-			//If an expiration date is set store it
99
-			if ($expirationDate !== null) {
100
-				$qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime'));
101
-			}
102
-
103
-			$qb->setValue('reminder_sent', $qb->createNamedParameter($share->getReminderSent(), IQueryBuilder::PARAM_BOOL));
104
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
105
-			//Set the GID of the group we share with
106
-			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
107
-
108
-			//If an expiration date is set store it
109
-			if ($expirationDate !== null) {
110
-				$qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime'));
111
-			}
112
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
113
-			//set label for public link
114
-			$qb->setValue('label', $qb->createNamedParameter($share->getLabel()));
115
-			//Set the token of the share
116
-			$qb->setValue('token', $qb->createNamedParameter($share->getToken()));
117
-
118
-			//If a password is set store it
119
-			if ($share->getPassword() !== null) {
120
-				$qb->setValue('password', $qb->createNamedParameter($share->getPassword()));
121
-			}
122
-
123
-			$qb->setValue('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL));
124
-
125
-			//If an expiration date is set store it
126
-			if ($expirationDate !== null) {
127
-				$qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime'));
128
-			}
129
-
130
-			$qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
131
-
132
-			$qb->setValue('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0, IQueryBuilder::PARAM_INT));
133
-		} else {
134
-			throw new \Exception('invalid share type!');
135
-		}
136
-
137
-		// Set what is shares
138
-		$qb->setValue('item_type', $qb->createParameter('itemType'));
139
-		if ($share->getNode() instanceof \OCP\Files\File) {
140
-			$qb->setParameter('itemType', 'file');
141
-		} else {
142
-			$qb->setParameter('itemType', 'folder');
143
-		}
144
-
145
-		// Set the file id
146
-		$qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
147
-		$qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
148
-
149
-		// set the permissions
150
-		$qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
151
-
152
-		// set share attributes
153
-		$shareAttributes = $this->formatShareAttributes(
154
-			$share->getAttributes()
155
-		);
156
-		$qb->setValue('attributes', $qb->createNamedParameter($shareAttributes));
157
-
158
-		// Set who created this share
159
-		$qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
160
-
161
-		// Set who is the owner of this file/folder (and this the owner of the share)
162
-		$qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
163
-
164
-		// Set the file target
165
-		$qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
166
-
167
-		if ($share->getNote() !== '') {
168
-			$qb->setValue('note', $qb->createNamedParameter($share->getNote()));
169
-		}
170
-
171
-		// Set the time this share was created
172
-		$shareTime = $this->timeFactory->now();
173
-		$qb->setValue('stime', $qb->createNamedParameter($shareTime->getTimestamp()));
174
-
175
-		// insert the data and fetch the id of the share
176
-		$qb->executeStatement();
177
-
178
-		// Update mandatory data
179
-		$id = $qb->getLastInsertId();
180
-		$share->setId((string)$id);
181
-		$share->setProviderId($this->identifier());
182
-
183
-		$share->setShareTime(\DateTime::createFromImmutable($shareTime));
184
-
185
-		$mailSendValue = $share->getMailSend();
186
-		$share->setMailSend(($mailSendValue === null) ? true : $mailSendValue);
187
-
188
-		return $share;
189
-	}
190
-
191
-	/**
192
-	 * Update a share
193
-	 *
194
-	 * @param \OCP\Share\IShare $share
195
-	 * @return \OCP\Share\IShare The share object
196
-	 * @throws ShareNotFound
197
-	 * @throws \OCP\Files\InvalidPathException
198
-	 * @throws \OCP\Files\NotFoundException
199
-	 */
200
-	public function update(\OCP\Share\IShare $share) {
201
-		$originalShare = $this->getShareById($share->getId());
202
-
203
-		$shareAttributes = $this->formatShareAttributes($share->getAttributes());
204
-
205
-		$expirationDate = $share->getExpirationDate();
206
-		if ($expirationDate !== null) {
207
-			$expirationDate = clone $expirationDate;
208
-			$expirationDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
209
-		}
210
-
211
-		if ($share->getShareType() === IShare::TYPE_USER) {
212
-			/*
48
+    public function __construct(
49
+        private IDBConnection $dbConn,
50
+        private IUserManager $userManager,
51
+        private IGroupManager $groupManager,
52
+        private IRootFolder $rootFolder,
53
+        private IMailer $mailer,
54
+        private Defaults $defaults,
55
+        private IFactory $l10nFactory,
56
+        private IURLGenerator $urlGenerator,
57
+        private ITimeFactory $timeFactory,
58
+        private LoggerInterface $logger,
59
+        private IManager $shareManager,
60
+        private IConfig $config,
61
+    ) {
62
+    }
63
+
64
+    /**
65
+     * Return the identifier of this provider.
66
+     *
67
+     * @return string Containing only [a-zA-Z0-9]
68
+     */
69
+    public function identifier() {
70
+        return 'ocinternal';
71
+    }
72
+
73
+    /**
74
+     * Share a path
75
+     *
76
+     * @param \OCP\Share\IShare $share
77
+     * @return \OCP\Share\IShare The share object
78
+     * @throws ShareNotFound
79
+     * @throws \Exception
80
+     */
81
+    public function create(\OCP\Share\IShare $share) {
82
+        $qb = $this->dbConn->getQueryBuilder();
83
+
84
+        $qb->insert('share');
85
+        $qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
86
+
87
+        $expirationDate = $share->getExpirationDate();
88
+        if ($expirationDate !== null) {
89
+            $expirationDate = clone $expirationDate;
90
+            $expirationDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
91
+        }
92
+
93
+        if ($share->getShareType() === IShare::TYPE_USER) {
94
+            //Set the UID of the user we share with
95
+            $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
96
+            $qb->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_PENDING));
97
+
98
+            //If an expiration date is set store it
99
+            if ($expirationDate !== null) {
100
+                $qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime'));
101
+            }
102
+
103
+            $qb->setValue('reminder_sent', $qb->createNamedParameter($share->getReminderSent(), IQueryBuilder::PARAM_BOOL));
104
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
105
+            //Set the GID of the group we share with
106
+            $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
107
+
108
+            //If an expiration date is set store it
109
+            if ($expirationDate !== null) {
110
+                $qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime'));
111
+            }
112
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
113
+            //set label for public link
114
+            $qb->setValue('label', $qb->createNamedParameter($share->getLabel()));
115
+            //Set the token of the share
116
+            $qb->setValue('token', $qb->createNamedParameter($share->getToken()));
117
+
118
+            //If a password is set store it
119
+            if ($share->getPassword() !== null) {
120
+                $qb->setValue('password', $qb->createNamedParameter($share->getPassword()));
121
+            }
122
+
123
+            $qb->setValue('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL));
124
+
125
+            //If an expiration date is set store it
126
+            if ($expirationDate !== null) {
127
+                $qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime'));
128
+            }
129
+
130
+            $qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
131
+
132
+            $qb->setValue('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0, IQueryBuilder::PARAM_INT));
133
+        } else {
134
+            throw new \Exception('invalid share type!');
135
+        }
136
+
137
+        // Set what is shares
138
+        $qb->setValue('item_type', $qb->createParameter('itemType'));
139
+        if ($share->getNode() instanceof \OCP\Files\File) {
140
+            $qb->setParameter('itemType', 'file');
141
+        } else {
142
+            $qb->setParameter('itemType', 'folder');
143
+        }
144
+
145
+        // Set the file id
146
+        $qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
147
+        $qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
148
+
149
+        // set the permissions
150
+        $qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
151
+
152
+        // set share attributes
153
+        $shareAttributes = $this->formatShareAttributes(
154
+            $share->getAttributes()
155
+        );
156
+        $qb->setValue('attributes', $qb->createNamedParameter($shareAttributes));
157
+
158
+        // Set who created this share
159
+        $qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
160
+
161
+        // Set who is the owner of this file/folder (and this the owner of the share)
162
+        $qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
163
+
164
+        // Set the file target
165
+        $qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
166
+
167
+        if ($share->getNote() !== '') {
168
+            $qb->setValue('note', $qb->createNamedParameter($share->getNote()));
169
+        }
170
+
171
+        // Set the time this share was created
172
+        $shareTime = $this->timeFactory->now();
173
+        $qb->setValue('stime', $qb->createNamedParameter($shareTime->getTimestamp()));
174
+
175
+        // insert the data and fetch the id of the share
176
+        $qb->executeStatement();
177
+
178
+        // Update mandatory data
179
+        $id = $qb->getLastInsertId();
180
+        $share->setId((string)$id);
181
+        $share->setProviderId($this->identifier());
182
+
183
+        $share->setShareTime(\DateTime::createFromImmutable($shareTime));
184
+
185
+        $mailSendValue = $share->getMailSend();
186
+        $share->setMailSend(($mailSendValue === null) ? true : $mailSendValue);
187
+
188
+        return $share;
189
+    }
190
+
191
+    /**
192
+     * Update a share
193
+     *
194
+     * @param \OCP\Share\IShare $share
195
+     * @return \OCP\Share\IShare The share object
196
+     * @throws ShareNotFound
197
+     * @throws \OCP\Files\InvalidPathException
198
+     * @throws \OCP\Files\NotFoundException
199
+     */
200
+    public function update(\OCP\Share\IShare $share) {
201
+        $originalShare = $this->getShareById($share->getId());
202
+
203
+        $shareAttributes = $this->formatShareAttributes($share->getAttributes());
204
+
205
+        $expirationDate = $share->getExpirationDate();
206
+        if ($expirationDate !== null) {
207
+            $expirationDate = clone $expirationDate;
208
+            $expirationDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
209
+        }
210
+
211
+        if ($share->getShareType() === IShare::TYPE_USER) {
212
+            /*
213 213
 			 * We allow updating the recipient on user shares.
214 214
 			 */
215
-			$qb = $this->dbConn->getQueryBuilder();
216
-			$qb->update('share')
217
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
218
-				->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
219
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
220
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
221
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
222
-				->set('attributes', $qb->createNamedParameter($shareAttributes))
223
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
224
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
225
-				->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
226
-				->set('note', $qb->createNamedParameter($share->getNote()))
227
-				->set('accepted', $qb->createNamedParameter($share->getStatus()))
228
-				->set('reminder_sent', $qb->createNamedParameter($share->getReminderSent(), IQueryBuilder::PARAM_BOOL))
229
-				->executeStatement();
230
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
231
-			$qb = $this->dbConn->getQueryBuilder();
232
-			$qb->update('share')
233
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
234
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
235
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
236
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
237
-				->set('attributes', $qb->createNamedParameter($shareAttributes))
238
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
239
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
240
-				->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
241
-				->set('note', $qb->createNamedParameter($share->getNote()))
242
-				->executeStatement();
243
-
244
-			/*
215
+            $qb = $this->dbConn->getQueryBuilder();
216
+            $qb->update('share')
217
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
218
+                ->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
219
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
220
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
221
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
222
+                ->set('attributes', $qb->createNamedParameter($shareAttributes))
223
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
224
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
225
+                ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
226
+                ->set('note', $qb->createNamedParameter($share->getNote()))
227
+                ->set('accepted', $qb->createNamedParameter($share->getStatus()))
228
+                ->set('reminder_sent', $qb->createNamedParameter($share->getReminderSent(), IQueryBuilder::PARAM_BOOL))
229
+                ->executeStatement();
230
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
231
+            $qb = $this->dbConn->getQueryBuilder();
232
+            $qb->update('share')
233
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
234
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
235
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
236
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
237
+                ->set('attributes', $qb->createNamedParameter($shareAttributes))
238
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
239
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
240
+                ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
241
+                ->set('note', $qb->createNamedParameter($share->getNote()))
242
+                ->executeStatement();
243
+
244
+            /*
245 245
 			 * Update all user defined group shares
246 246
 			 */
247
-			$qb = $this->dbConn->getQueryBuilder();
248
-			$qb->update('share')
249
-				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
250
-				->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
251
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
252
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
253
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
254
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
255
-				->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
256
-				->set('note', $qb->createNamedParameter($share->getNote()))
257
-				->executeStatement();
258
-
259
-			/*
247
+            $qb = $this->dbConn->getQueryBuilder();
248
+            $qb->update('share')
249
+                ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
250
+                ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
251
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
252
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
253
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
254
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
255
+                ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
256
+                ->set('note', $qb->createNamedParameter($share->getNote()))
257
+                ->executeStatement();
258
+
259
+            /*
260 260
 			 * Now update the permissions for all children that have not set it to 0
261 261
 			 */
262
-			$qb = $this->dbConn->getQueryBuilder();
263
-			$qb->update('share')
264
-				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
265
-				->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
266
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
267
-				->set('attributes', $qb->createNamedParameter($shareAttributes))
268
-				->executeStatement();
269
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
270
-			$qb = $this->dbConn->getQueryBuilder();
271
-			$qb->update('share')
272
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
273
-				->set('password', $qb->createNamedParameter($share->getPassword()))
274
-				->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL))
275
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
276
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
277
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
278
-				->set('attributes', $qb->createNamedParameter($shareAttributes))
279
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
280
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
281
-				->set('token', $qb->createNamedParameter($share->getToken()))
282
-				->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
283
-				->set('note', $qb->createNamedParameter($share->getNote()))
284
-				->set('label', $qb->createNamedParameter($share->getLabel()))
285
-				->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0, IQueryBuilder::PARAM_INT))
286
-				->executeStatement();
287
-		}
288
-
289
-		if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') {
290
-			$this->propagateNote($share);
291
-		}
292
-
293
-
294
-		return $share;
295
-	}
296
-
297
-	/**
298
-	 * Accept a share.
299
-	 *
300
-	 * @param IShare $share
301
-	 * @param string $recipient
302
-	 * @return IShare The share object
303
-	 * @since 9.0.0
304
-	 */
305
-	public function acceptShare(IShare $share, string $recipient): IShare {
306
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
307
-			$group = $this->groupManager->get($share->getSharedWith());
308
-			$user = $this->userManager->get($recipient);
309
-
310
-			if (is_null($group)) {
311
-				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
312
-			}
313
-
314
-			if (!$group->inGroup($user)) {
315
-				throw new ProviderException('Recipient not in receiving group');
316
-			}
317
-
318
-			// Try to fetch user specific share
319
-			$qb = $this->dbConn->getQueryBuilder();
320
-			$stmt = $qb->select('*')
321
-				->from('share')
322
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
323
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
324
-				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
325
-				->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
326
-				->executeQuery();
327
-
328
-			$data = $stmt->fetch();
329
-			$stmt->closeCursor();
330
-
331
-			/*
262
+            $qb = $this->dbConn->getQueryBuilder();
263
+            $qb->update('share')
264
+                ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
265
+                ->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
266
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
267
+                ->set('attributes', $qb->createNamedParameter($shareAttributes))
268
+                ->executeStatement();
269
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
270
+            $qb = $this->dbConn->getQueryBuilder();
271
+            $qb->update('share')
272
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
273
+                ->set('password', $qb->createNamedParameter($share->getPassword()))
274
+                ->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL))
275
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
276
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
277
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
278
+                ->set('attributes', $qb->createNamedParameter($shareAttributes))
279
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
280
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
281
+                ->set('token', $qb->createNamedParameter($share->getToken()))
282
+                ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
283
+                ->set('note', $qb->createNamedParameter($share->getNote()))
284
+                ->set('label', $qb->createNamedParameter($share->getLabel()))
285
+                ->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0, IQueryBuilder::PARAM_INT))
286
+                ->executeStatement();
287
+        }
288
+
289
+        if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') {
290
+            $this->propagateNote($share);
291
+        }
292
+
293
+
294
+        return $share;
295
+    }
296
+
297
+    /**
298
+     * Accept a share.
299
+     *
300
+     * @param IShare $share
301
+     * @param string $recipient
302
+     * @return IShare The share object
303
+     * @since 9.0.0
304
+     */
305
+    public function acceptShare(IShare $share, string $recipient): IShare {
306
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
307
+            $group = $this->groupManager->get($share->getSharedWith());
308
+            $user = $this->userManager->get($recipient);
309
+
310
+            if (is_null($group)) {
311
+                throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
312
+            }
313
+
314
+            if (!$group->inGroup($user)) {
315
+                throw new ProviderException('Recipient not in receiving group');
316
+            }
317
+
318
+            // Try to fetch user specific share
319
+            $qb = $this->dbConn->getQueryBuilder();
320
+            $stmt = $qb->select('*')
321
+                ->from('share')
322
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
323
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
324
+                ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
325
+                ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
326
+                ->executeQuery();
327
+
328
+            $data = $stmt->fetch();
329
+            $stmt->closeCursor();
330
+
331
+            /*
332 332
 			 * Check if there already is a user specific group share.
333 333
 			 * If there is update it (if required).
334 334
 			 */
335
-			if ($data === false) {
336
-				$id = $this->createUserSpecificGroupShare($share, $recipient);
337
-			} else {
338
-				$id = $data['id'];
339
-			}
340
-		} elseif ($share->getShareType() === IShare::TYPE_USER) {
341
-			if ($share->getSharedWith() !== $recipient) {
342
-				throw new ProviderException('Recipient does not match');
343
-			}
344
-
345
-			$id = $share->getId();
346
-		} else {
347
-			throw new ProviderException('Invalid shareType');
348
-		}
349
-
350
-		$qb = $this->dbConn->getQueryBuilder();
351
-		$qb->update('share')
352
-			->set('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED))
353
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
354
-			->executeStatement();
355
-
356
-		return $share;
357
-	}
358
-
359
-	public function getChildren(IShare $parent): array {
360
-		$children = [];
361
-
362
-		$qb = $this->dbConn->getQueryBuilder();
363
-		$qb->select('*')
364
-			->from('share')
365
-			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
366
-			->andWhere(
367
-				$qb->expr()->in(
368
-					'share_type',
369
-					$qb->createNamedParameter([
370
-						IShare::TYPE_USER,
371
-						IShare::TYPE_GROUP,
372
-						IShare::TYPE_LINK,
373
-					], IQueryBuilder::PARAM_INT_ARRAY)
374
-				)
375
-			)
376
-			->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
377
-			->orderBy('id');
378
-
379
-		$cursor = $qb->executeQuery();
380
-		while ($data = $cursor->fetch()) {
381
-			$children[] = $this->createShare($data);
382
-		}
383
-		$cursor->closeCursor();
384
-
385
-		return $children;
386
-	}
387
-
388
-	/**
389
-	 * Delete a share
390
-	 *
391
-	 * @param \OCP\Share\IShare $share
392
-	 */
393
-	public function delete(\OCP\Share\IShare $share) {
394
-		$qb = $this->dbConn->getQueryBuilder();
395
-		$qb->delete('share')
396
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
397
-
398
-		/*
335
+            if ($data === false) {
336
+                $id = $this->createUserSpecificGroupShare($share, $recipient);
337
+            } else {
338
+                $id = $data['id'];
339
+            }
340
+        } elseif ($share->getShareType() === IShare::TYPE_USER) {
341
+            if ($share->getSharedWith() !== $recipient) {
342
+                throw new ProviderException('Recipient does not match');
343
+            }
344
+
345
+            $id = $share->getId();
346
+        } else {
347
+            throw new ProviderException('Invalid shareType');
348
+        }
349
+
350
+        $qb = $this->dbConn->getQueryBuilder();
351
+        $qb->update('share')
352
+            ->set('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED))
353
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
354
+            ->executeStatement();
355
+
356
+        return $share;
357
+    }
358
+
359
+    public function getChildren(IShare $parent): array {
360
+        $children = [];
361
+
362
+        $qb = $this->dbConn->getQueryBuilder();
363
+        $qb->select('*')
364
+            ->from('share')
365
+            ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
366
+            ->andWhere(
367
+                $qb->expr()->in(
368
+                    'share_type',
369
+                    $qb->createNamedParameter([
370
+                        IShare::TYPE_USER,
371
+                        IShare::TYPE_GROUP,
372
+                        IShare::TYPE_LINK,
373
+                    ], IQueryBuilder::PARAM_INT_ARRAY)
374
+                )
375
+            )
376
+            ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
377
+            ->orderBy('id');
378
+
379
+        $cursor = $qb->executeQuery();
380
+        while ($data = $cursor->fetch()) {
381
+            $children[] = $this->createShare($data);
382
+        }
383
+        $cursor->closeCursor();
384
+
385
+        return $children;
386
+    }
387
+
388
+    /**
389
+     * Delete a share
390
+     *
391
+     * @param \OCP\Share\IShare $share
392
+     */
393
+    public function delete(\OCP\Share\IShare $share) {
394
+        $qb = $this->dbConn->getQueryBuilder();
395
+        $qb->delete('share')
396
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
397
+
398
+        /*
399 399
 		 * If the share is a group share delete all possible
400 400
 		 * user defined groups shares.
401 401
 		 */
402
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
403
-			$qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
404
-		}
405
-
406
-		$qb->executeStatement();
407
-	}
408
-
409
-	/**
410
-	 * Unshare a share from the recipient. If this is a group share
411
-	 * this means we need a special entry in the share db.
412
-	 *
413
-	 * @param IShare $share
414
-	 * @param string $recipient UserId of recipient
415
-	 * @throws BackendError
416
-	 * @throws ProviderException
417
-	 */
418
-	public function deleteFromSelf(IShare $share, $recipient) {
419
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
420
-			$group = $this->groupManager->get($share->getSharedWith());
421
-			$user = $this->userManager->get($recipient);
422
-
423
-			if (is_null($group)) {
424
-				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
425
-			}
426
-
427
-			if (!$group->inGroup($user)) {
428
-				// nothing left to do
429
-				return;
430
-			}
431
-
432
-			// Try to fetch user specific share
433
-			$qb = $this->dbConn->getQueryBuilder();
434
-			$stmt = $qb->select('*')
435
-				->from('share')
436
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
437
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
438
-				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
439
-				->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
440
-				->executeQuery();
441
-
442
-			$data = $stmt->fetch();
443
-
444
-			/*
402
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
403
+            $qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
404
+        }
405
+
406
+        $qb->executeStatement();
407
+    }
408
+
409
+    /**
410
+     * Unshare a share from the recipient. If this is a group share
411
+     * this means we need a special entry in the share db.
412
+     *
413
+     * @param IShare $share
414
+     * @param string $recipient UserId of recipient
415
+     * @throws BackendError
416
+     * @throws ProviderException
417
+     */
418
+    public function deleteFromSelf(IShare $share, $recipient) {
419
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
420
+            $group = $this->groupManager->get($share->getSharedWith());
421
+            $user = $this->userManager->get($recipient);
422
+
423
+            if (is_null($group)) {
424
+                throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
425
+            }
426
+
427
+            if (!$group->inGroup($user)) {
428
+                // nothing left to do
429
+                return;
430
+            }
431
+
432
+            // Try to fetch user specific share
433
+            $qb = $this->dbConn->getQueryBuilder();
434
+            $stmt = $qb->select('*')
435
+                ->from('share')
436
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
437
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
438
+                ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
439
+                ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
440
+                ->executeQuery();
441
+
442
+            $data = $stmt->fetch();
443
+
444
+            /*
445 445
 			 * Check if there already is a user specific group share.
446 446
 			 * If there is update it (if required).
447 447
 			 */
448
-			if ($data === false) {
449
-				$id = $this->createUserSpecificGroupShare($share, $recipient);
450
-				$permissions = $share->getPermissions();
451
-			} else {
452
-				$permissions = $data['permissions'];
453
-				$id = $data['id'];
454
-			}
455
-
456
-			if ($permissions !== 0) {
457
-				// Update existing usergroup share
458
-				$qb = $this->dbConn->getQueryBuilder();
459
-				$qb->update('share')
460
-					->set('permissions', $qb->createNamedParameter(0))
461
-					->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
462
-					->executeStatement();
463
-			}
464
-		} elseif ($share->getShareType() === IShare::TYPE_USER) {
465
-			if ($share->getSharedWith() !== $recipient) {
466
-				throw new ProviderException('Recipient does not match');
467
-			}
468
-
469
-			// We can just delete user and link shares
470
-			$this->delete($share);
471
-		} else {
472
-			throw new ProviderException('Invalid shareType');
473
-		}
474
-	}
475
-
476
-	protected function createUserSpecificGroupShare(IShare $share, string $recipient): int {
477
-		$type = $share->getNodeType();
478
-
479
-		$shareFolder = $this->config->getSystemValue('share_folder', '/');
480
-		$allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true);
481
-		if ($allowCustomShareFolder) {
482
-			$shareFolder = $this->config->getUserValue($recipient, Application::APP_ID, 'share_folder', $shareFolder);
483
-		}
484
-
485
-		$target = $shareFolder . '/' . $share->getNode()->getName();
486
-		$target = \OC\Files\Filesystem::normalizePath($target);
487
-
488
-		$qb = $this->dbConn->getQueryBuilder();
489
-		$qb->insert('share')
490
-			->values([
491
-				'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
492
-				'share_with' => $qb->createNamedParameter($recipient),
493
-				'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
494
-				'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
495
-				'parent' => $qb->createNamedParameter($share->getId()),
496
-				'item_type' => $qb->createNamedParameter($type),
497
-				'item_source' => $qb->createNamedParameter($share->getNodeId()),
498
-				'file_source' => $qb->createNamedParameter($share->getNodeId()),
499
-				'file_target' => $qb->createNamedParameter($target),
500
-				'permissions' => $qb->createNamedParameter($share->getPermissions()),
501
-				'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
502
-			])->executeStatement();
503
-
504
-		return $qb->getLastInsertId();
505
-	}
506
-
507
-	/**
508
-	 * @inheritdoc
509
-	 *
510
-	 * For now this only works for group shares
511
-	 * If this gets implemented for normal shares we have to extend it
512
-	 */
513
-	public function restore(IShare $share, string $recipient): IShare {
514
-		$qb = $this->dbConn->getQueryBuilder();
515
-		$qb->select('permissions')
516
-			->from('share')
517
-			->where(
518
-				$qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))
519
-			);
520
-		$cursor = $qb->executeQuery();
521
-		$data = $cursor->fetch();
522
-		$cursor->closeCursor();
523
-
524
-		$originalPermission = $data['permissions'];
525
-
526
-		$qb = $this->dbConn->getQueryBuilder();
527
-		$qb->update('share')
528
-			->set('permissions', $qb->createNamedParameter($originalPermission))
529
-			->where(
530
-				$qb->expr()->eq('parent', $qb->createNamedParameter($share->getParent()))
531
-			)->andWhere(
532
-				$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))
533
-			)->andWhere(
534
-				$qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))
535
-			);
536
-
537
-		$qb->executeStatement();
538
-
539
-		return $this->getShareById($share->getId(), $recipient);
540
-	}
541
-
542
-	/**
543
-	 * @inheritdoc
544
-	 */
545
-	public function move(\OCP\Share\IShare $share, $recipient) {
546
-		if ($share->getShareType() === IShare::TYPE_USER) {
547
-			// Just update the target
548
-			$qb = $this->dbConn->getQueryBuilder();
549
-			$qb->update('share')
550
-				->set('file_target', $qb->createNamedParameter($share->getTarget()))
551
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
552
-				->executeStatement();
553
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
554
-			// Check if there is a usergroup share
555
-			$qb = $this->dbConn->getQueryBuilder();
556
-			$stmt = $qb->select('id')
557
-				->from('share')
558
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
559
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
560
-				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
561
-				->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
562
-				->setMaxResults(1)
563
-				->executeQuery();
564
-
565
-			$data = $stmt->fetch();
566
-			$stmt->closeCursor();
567
-
568
-			$shareAttributes = $this->formatShareAttributes(
569
-				$share->getAttributes()
570
-			);
571
-
572
-			if ($data === false) {
573
-				// No usergroup share yet. Create one.
574
-				$qb = $this->dbConn->getQueryBuilder();
575
-				$qb->insert('share')
576
-					->values([
577
-						'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
578
-						'share_with' => $qb->createNamedParameter($recipient),
579
-						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
580
-						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
581
-						'parent' => $qb->createNamedParameter($share->getId()),
582
-						'item_type' => $qb->createNamedParameter($share->getNodeType()),
583
-						'item_source' => $qb->createNamedParameter($share->getNodeId()),
584
-						'file_source' => $qb->createNamedParameter($share->getNodeId()),
585
-						'file_target' => $qb->createNamedParameter($share->getTarget()),
586
-						'permissions' => $qb->createNamedParameter($share->getPermissions()),
587
-						'attributes' => $qb->createNamedParameter($shareAttributes),
588
-						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
589
-					])->executeStatement();
590
-			} else {
591
-				// Already a usergroup share. Update it.
592
-				$qb = $this->dbConn->getQueryBuilder();
593
-				$qb->update('share')
594
-					->set('file_target', $qb->createNamedParameter($share->getTarget()))
595
-					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
596
-					->executeStatement();
597
-			}
598
-		}
599
-
600
-		return $share;
601
-	}
602
-
603
-	public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) {
604
-		if (!$shallow) {
605
-			throw new \Exception('non-shallow getSharesInFolder is no longer supported');
606
-		}
607
-
608
-		return $this->getSharesInFolderInternal($userId, $node, $reshares);
609
-	}
610
-
611
-	public function getAllSharesInFolder(Folder $node): array {
612
-		return $this->getSharesInFolderInternal(null, $node, null);
613
-	}
614
-
615
-	/**
616
-	 * @return array<int, list<IShare>>
617
-	 */
618
-	private function getSharesInFolderInternal(?string $userId, Folder $node, ?bool $reshares): array {
619
-		$qb = $this->dbConn->getQueryBuilder();
620
-		$qb->select('s.*',
621
-			'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
622
-			'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
623
-			'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum')
624
-			->from('share', 's')
625
-			->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
626
-
627
-		$qb->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK], IQueryBuilder::PARAM_INT_ARRAY)));
628
-
629
-		if ($userId !== null) {
630
-			/**
631
-			 * Reshares for this user are shares where they are the owner.
632
-			 */
633
-			if ($reshares !== true) {
634
-				$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
635
-			} else {
636
-				$qb->andWhere(
637
-					$qb->expr()->orX(
638
-						$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
639
-						$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
640
-					)
641
-				);
642
-			}
643
-		}
644
-
645
-		// todo? maybe get these from the oc_mounts table
646
-		$childMountNodes = array_filter($node->getDirectoryListing(), function (Node $node): bool {
647
-			return $node->getInternalPath() === '';
648
-		});
649
-		$childMountRootIds = array_map(function (Node $node): int {
650
-			return $node->getId();
651
-		}, $childMountNodes);
652
-
653
-		$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
654
-		$qb->andWhere(
655
-			$qb->expr()->orX(
656
-				$qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())),
657
-				$qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
658
-			)
659
-		);
660
-
661
-		$qb->orderBy('id');
662
-
663
-		$shares = [];
664
-
665
-		$chunks = array_chunk($childMountRootIds, 1000);
666
-
667
-		// Force the request to be run when there is 0 mount.
668
-		if (count($chunks) === 0) {
669
-			$chunks = [[]];
670
-		}
671
-
672
-		foreach ($chunks as $chunk) {
673
-			$qb->setParameter('chunk', $chunk, IQueryBuilder::PARAM_INT_ARRAY);
674
-			$cursor = $qb->executeQuery();
675
-			while ($data = $cursor->fetch()) {
676
-				$shares[$data['fileid']][] = $this->createShare($data);
677
-			}
678
-			$cursor->closeCursor();
679
-		}
680
-
681
-		return $shares;
682
-	}
683
-
684
-	/**
685
-	 * @inheritdoc
686
-	 */
687
-	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
688
-		$qb = $this->dbConn->getQueryBuilder();
689
-		$qb->select('*')
690
-			->from('share')
691
-			->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
692
-
693
-		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
694
-
695
-		/**
696
-		 * Reshares for this user are shares where they are the owner.
697
-		 */
698
-		if ($reshares === false) {
699
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
700
-		} else {
701
-			if ($node === null) {
702
-				$qb->andWhere(
703
-					$qb->expr()->orX(
704
-						$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
705
-						$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
706
-					)
707
-				);
708
-			}
709
-		}
710
-
711
-		if ($node !== null) {
712
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
713
-		}
714
-
715
-		if ($limit !== -1) {
716
-			$qb->setMaxResults($limit);
717
-		}
718
-
719
-		$qb->setFirstResult($offset);
720
-		$qb->orderBy('id');
721
-
722
-		$cursor = $qb->executeQuery();
723
-		$shares = [];
724
-		while ($data = $cursor->fetch()) {
725
-			$shares[] = $this->createShare($data);
726
-		}
727
-		$cursor->closeCursor();
728
-
729
-		return $shares;
730
-	}
731
-
732
-	/**
733
-	 * @inheritdoc
734
-	 */
735
-	public function getShareById($id, $recipientId = null) {
736
-		$qb = $this->dbConn->getQueryBuilder();
737
-
738
-		$qb->select('*')
739
-			->from('share')
740
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
741
-			->andWhere(
742
-				$qb->expr()->in(
743
-					'share_type',
744
-					$qb->createNamedParameter([
745
-						IShare::TYPE_USER,
746
-						IShare::TYPE_GROUP,
747
-						IShare::TYPE_LINK,
748
-					], IQueryBuilder::PARAM_INT_ARRAY)
749
-				)
750
-			)
751
-			->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
752
-
753
-		$cursor = $qb->executeQuery();
754
-		$data = $cursor->fetch();
755
-		$cursor->closeCursor();
756
-
757
-		if ($data === false) {
758
-			throw new ShareNotFound();
759
-		}
760
-
761
-		try {
762
-			$share = $this->createShare($data);
763
-		} catch (InvalidShare $e) {
764
-			throw new ShareNotFound();
765
-		}
766
-
767
-		// If the recipient is set for a group share resolve to that user
768
-		if ($recipientId !== null && $share->getShareType() === IShare::TYPE_GROUP) {
769
-			$share = $this->resolveGroupShares([(int)$share->getId() => $share], $recipientId)[0];
770
-		}
771
-
772
-		return $share;
773
-	}
774
-
775
-	/**
776
-	 * Get shares for a given path
777
-	 *
778
-	 * @param \OCP\Files\Node $path
779
-	 * @return \OCP\Share\IShare[]
780
-	 */
781
-	public function getSharesByPath(Node $path) {
782
-		$qb = $this->dbConn->getQueryBuilder();
783
-
784
-		$cursor = $qb->select('*')
785
-			->from('share')
786
-			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
787
-			->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK], IQueryBuilder::PARAM_INT_ARRAY)))
788
-			->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
789
-			->orderBy('id', 'ASC')
790
-			->executeQuery();
791
-
792
-		$shares = [];
793
-		while ($data = $cursor->fetch()) {
794
-			$shares[] = $this->createShare($data);
795
-		}
796
-		$cursor->closeCursor();
797
-
798
-		return $shares;
799
-	}
800
-
801
-	/**
802
-	 * Returns whether the given database result can be interpreted as
803
-	 * a share with accessible file (not trashed, not deleted)
804
-	 */
805
-	private function isAccessibleResult($data) {
806
-		// exclude shares leading to deleted file entries
807
-		if ($data['fileid'] === null || $data['path'] === null) {
808
-			return false;
809
-		}
810
-
811
-		// exclude shares leading to trashbin on home storages
812
-		$pathSections = explode('/', $data['path'], 2);
813
-		// FIXME: would not detect rare md5'd home storage case properly
814
-		if ($pathSections[0] !== 'files'
815
-			&& (str_starts_with($data['storage_string_id'], 'home::') || str_starts_with($data['storage_string_id'], 'object::user'))) {
816
-			return false;
817
-		} elseif ($pathSections[0] === '__groupfolders'
818
-			&& str_starts_with($pathSections[1], 'trash/')
819
-		) {
820
-			// exclude shares leading to trashbin on group folders storages
821
-			return false;
822
-		}
823
-		return true;
824
-	}
825
-
826
-	/**
827
-	 * @inheritdoc
828
-	 */
829
-	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
830
-		/** @var Share[] $shares */
831
-		$shares = [];
832
-
833
-		if ($shareType === IShare::TYPE_USER) {
834
-			//Get shares directly with this user
835
-			$qb = $this->dbConn->getQueryBuilder();
836
-			$qb->select('s.*',
837
-				'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
838
-				'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
839
-				'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
840
-			)
841
-				->selectAlias('st.id', 'storage_string_id')
842
-				->from('share', 's')
843
-				->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
844
-				->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
845
-
846
-			// Order by id
847
-			$qb->orderBy('s.id');
848
-
849
-			// Set limit and offset
850
-			if ($limit !== -1) {
851
-				$qb->setMaxResults($limit);
852
-			}
853
-			$qb->setFirstResult($offset);
854
-
855
-			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)))
856
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
857
-				->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
858
-
859
-			// Filter by node if provided
860
-			if ($node !== null) {
861
-				$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
862
-			}
863
-
864
-			$cursor = $qb->executeQuery();
865
-
866
-			while ($data = $cursor->fetch()) {
867
-				if ($data['fileid'] && $data['path'] === null) {
868
-					$data['path'] = (string)$data['path'];
869
-					$data['name'] = (string)$data['name'];
870
-					$data['checksum'] = (string)$data['checksum'];
871
-				}
872
-				if ($this->isAccessibleResult($data)) {
873
-					$shares[] = $this->createShare($data);
874
-				}
875
-			}
876
-			$cursor->closeCursor();
877
-		} elseif ($shareType === IShare::TYPE_GROUP) {
878
-			$user = new LazyUser($userId, $this->userManager);
879
-			$allGroups = $this->groupManager->getUserGroupIds($user);
880
-
881
-			/** @var Share[] $shares2 */
882
-			$shares2 = [];
883
-
884
-			$start = 0;
885
-			while (true) {
886
-				$groups = array_slice($allGroups, $start, 1000);
887
-				$start += 1000;
888
-
889
-				if ($groups === []) {
890
-					break;
891
-				}
892
-
893
-				$qb = $this->dbConn->getQueryBuilder();
894
-				$qb->select('s.*',
895
-					'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
896
-					'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
897
-					'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
898
-				)
899
-					->selectAlias('st.id', 'storage_string_id')
900
-					->from('share', 's')
901
-					->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
902
-					->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
903
-					->orderBy('s.id')
904
-					->setFirstResult(0);
905
-
906
-				if ($limit !== -1) {
907
-					$qb->setMaxResults($limit - count($shares));
908
-				}
909
-
910
-				// Filter by node if provided
911
-				if ($node !== null) {
912
-					$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
913
-				}
914
-
915
-				$groups = array_filter($groups);
916
-
917
-				$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
918
-					->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
919
-						$groups,
920
-						IQueryBuilder::PARAM_STR_ARRAY
921
-					)))
922
-					->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
923
-
924
-				$cursor = $qb->executeQuery();
925
-				while ($data = $cursor->fetch()) {
926
-					if ($offset > 0) {
927
-						$offset--;
928
-						continue;
929
-					}
930
-
931
-					if ($this->isAccessibleResult($data)) {
932
-						$share = $this->createShare($data);
933
-						$shares2[$share->getId()] = $share;
934
-					}
935
-				}
936
-				$cursor->closeCursor();
937
-			}
938
-
939
-			/*
448
+            if ($data === false) {
449
+                $id = $this->createUserSpecificGroupShare($share, $recipient);
450
+                $permissions = $share->getPermissions();
451
+            } else {
452
+                $permissions = $data['permissions'];
453
+                $id = $data['id'];
454
+            }
455
+
456
+            if ($permissions !== 0) {
457
+                // Update existing usergroup share
458
+                $qb = $this->dbConn->getQueryBuilder();
459
+                $qb->update('share')
460
+                    ->set('permissions', $qb->createNamedParameter(0))
461
+                    ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
462
+                    ->executeStatement();
463
+            }
464
+        } elseif ($share->getShareType() === IShare::TYPE_USER) {
465
+            if ($share->getSharedWith() !== $recipient) {
466
+                throw new ProviderException('Recipient does not match');
467
+            }
468
+
469
+            // We can just delete user and link shares
470
+            $this->delete($share);
471
+        } else {
472
+            throw new ProviderException('Invalid shareType');
473
+        }
474
+    }
475
+
476
+    protected function createUserSpecificGroupShare(IShare $share, string $recipient): int {
477
+        $type = $share->getNodeType();
478
+
479
+        $shareFolder = $this->config->getSystemValue('share_folder', '/');
480
+        $allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true);
481
+        if ($allowCustomShareFolder) {
482
+            $shareFolder = $this->config->getUserValue($recipient, Application::APP_ID, 'share_folder', $shareFolder);
483
+        }
484
+
485
+        $target = $shareFolder . '/' . $share->getNode()->getName();
486
+        $target = \OC\Files\Filesystem::normalizePath($target);
487
+
488
+        $qb = $this->dbConn->getQueryBuilder();
489
+        $qb->insert('share')
490
+            ->values([
491
+                'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
492
+                'share_with' => $qb->createNamedParameter($recipient),
493
+                'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
494
+                'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
495
+                'parent' => $qb->createNamedParameter($share->getId()),
496
+                'item_type' => $qb->createNamedParameter($type),
497
+                'item_source' => $qb->createNamedParameter($share->getNodeId()),
498
+                'file_source' => $qb->createNamedParameter($share->getNodeId()),
499
+                'file_target' => $qb->createNamedParameter($target),
500
+                'permissions' => $qb->createNamedParameter($share->getPermissions()),
501
+                'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
502
+            ])->executeStatement();
503
+
504
+        return $qb->getLastInsertId();
505
+    }
506
+
507
+    /**
508
+     * @inheritdoc
509
+     *
510
+     * For now this only works for group shares
511
+     * If this gets implemented for normal shares we have to extend it
512
+     */
513
+    public function restore(IShare $share, string $recipient): IShare {
514
+        $qb = $this->dbConn->getQueryBuilder();
515
+        $qb->select('permissions')
516
+            ->from('share')
517
+            ->where(
518
+                $qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))
519
+            );
520
+        $cursor = $qb->executeQuery();
521
+        $data = $cursor->fetch();
522
+        $cursor->closeCursor();
523
+
524
+        $originalPermission = $data['permissions'];
525
+
526
+        $qb = $this->dbConn->getQueryBuilder();
527
+        $qb->update('share')
528
+            ->set('permissions', $qb->createNamedParameter($originalPermission))
529
+            ->where(
530
+                $qb->expr()->eq('parent', $qb->createNamedParameter($share->getParent()))
531
+            )->andWhere(
532
+                $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))
533
+            )->andWhere(
534
+                $qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))
535
+            );
536
+
537
+        $qb->executeStatement();
538
+
539
+        return $this->getShareById($share->getId(), $recipient);
540
+    }
541
+
542
+    /**
543
+     * @inheritdoc
544
+     */
545
+    public function move(\OCP\Share\IShare $share, $recipient) {
546
+        if ($share->getShareType() === IShare::TYPE_USER) {
547
+            // Just update the target
548
+            $qb = $this->dbConn->getQueryBuilder();
549
+            $qb->update('share')
550
+                ->set('file_target', $qb->createNamedParameter($share->getTarget()))
551
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
552
+                ->executeStatement();
553
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
554
+            // Check if there is a usergroup share
555
+            $qb = $this->dbConn->getQueryBuilder();
556
+            $stmt = $qb->select('id')
557
+                ->from('share')
558
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
559
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
560
+                ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
561
+                ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
562
+                ->setMaxResults(1)
563
+                ->executeQuery();
564
+
565
+            $data = $stmt->fetch();
566
+            $stmt->closeCursor();
567
+
568
+            $shareAttributes = $this->formatShareAttributes(
569
+                $share->getAttributes()
570
+            );
571
+
572
+            if ($data === false) {
573
+                // No usergroup share yet. Create one.
574
+                $qb = $this->dbConn->getQueryBuilder();
575
+                $qb->insert('share')
576
+                    ->values([
577
+                        'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
578
+                        'share_with' => $qb->createNamedParameter($recipient),
579
+                        'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
580
+                        'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
581
+                        'parent' => $qb->createNamedParameter($share->getId()),
582
+                        'item_type' => $qb->createNamedParameter($share->getNodeType()),
583
+                        'item_source' => $qb->createNamedParameter($share->getNodeId()),
584
+                        'file_source' => $qb->createNamedParameter($share->getNodeId()),
585
+                        'file_target' => $qb->createNamedParameter($share->getTarget()),
586
+                        'permissions' => $qb->createNamedParameter($share->getPermissions()),
587
+                        'attributes' => $qb->createNamedParameter($shareAttributes),
588
+                        'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
589
+                    ])->executeStatement();
590
+            } else {
591
+                // Already a usergroup share. Update it.
592
+                $qb = $this->dbConn->getQueryBuilder();
593
+                $qb->update('share')
594
+                    ->set('file_target', $qb->createNamedParameter($share->getTarget()))
595
+                    ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
596
+                    ->executeStatement();
597
+            }
598
+        }
599
+
600
+        return $share;
601
+    }
602
+
603
+    public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) {
604
+        if (!$shallow) {
605
+            throw new \Exception('non-shallow getSharesInFolder is no longer supported');
606
+        }
607
+
608
+        return $this->getSharesInFolderInternal($userId, $node, $reshares);
609
+    }
610
+
611
+    public function getAllSharesInFolder(Folder $node): array {
612
+        return $this->getSharesInFolderInternal(null, $node, null);
613
+    }
614
+
615
+    /**
616
+     * @return array<int, list<IShare>>
617
+     */
618
+    private function getSharesInFolderInternal(?string $userId, Folder $node, ?bool $reshares): array {
619
+        $qb = $this->dbConn->getQueryBuilder();
620
+        $qb->select('s.*',
621
+            'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
622
+            'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
623
+            'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum')
624
+            ->from('share', 's')
625
+            ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
626
+
627
+        $qb->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK], IQueryBuilder::PARAM_INT_ARRAY)));
628
+
629
+        if ($userId !== null) {
630
+            /**
631
+             * Reshares for this user are shares where they are the owner.
632
+             */
633
+            if ($reshares !== true) {
634
+                $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
635
+            } else {
636
+                $qb->andWhere(
637
+                    $qb->expr()->orX(
638
+                        $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
639
+                        $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
640
+                    )
641
+                );
642
+            }
643
+        }
644
+
645
+        // todo? maybe get these from the oc_mounts table
646
+        $childMountNodes = array_filter($node->getDirectoryListing(), function (Node $node): bool {
647
+            return $node->getInternalPath() === '';
648
+        });
649
+        $childMountRootIds = array_map(function (Node $node): int {
650
+            return $node->getId();
651
+        }, $childMountNodes);
652
+
653
+        $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
654
+        $qb->andWhere(
655
+            $qb->expr()->orX(
656
+                $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())),
657
+                $qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
658
+            )
659
+        );
660
+
661
+        $qb->orderBy('id');
662
+
663
+        $shares = [];
664
+
665
+        $chunks = array_chunk($childMountRootIds, 1000);
666
+
667
+        // Force the request to be run when there is 0 mount.
668
+        if (count($chunks) === 0) {
669
+            $chunks = [[]];
670
+        }
671
+
672
+        foreach ($chunks as $chunk) {
673
+            $qb->setParameter('chunk', $chunk, IQueryBuilder::PARAM_INT_ARRAY);
674
+            $cursor = $qb->executeQuery();
675
+            while ($data = $cursor->fetch()) {
676
+                $shares[$data['fileid']][] = $this->createShare($data);
677
+            }
678
+            $cursor->closeCursor();
679
+        }
680
+
681
+        return $shares;
682
+    }
683
+
684
+    /**
685
+     * @inheritdoc
686
+     */
687
+    public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
688
+        $qb = $this->dbConn->getQueryBuilder();
689
+        $qb->select('*')
690
+            ->from('share')
691
+            ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
692
+
693
+        $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
694
+
695
+        /**
696
+         * Reshares for this user are shares where they are the owner.
697
+         */
698
+        if ($reshares === false) {
699
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
700
+        } else {
701
+            if ($node === null) {
702
+                $qb->andWhere(
703
+                    $qb->expr()->orX(
704
+                        $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
705
+                        $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
706
+                    )
707
+                );
708
+            }
709
+        }
710
+
711
+        if ($node !== null) {
712
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
713
+        }
714
+
715
+        if ($limit !== -1) {
716
+            $qb->setMaxResults($limit);
717
+        }
718
+
719
+        $qb->setFirstResult($offset);
720
+        $qb->orderBy('id');
721
+
722
+        $cursor = $qb->executeQuery();
723
+        $shares = [];
724
+        while ($data = $cursor->fetch()) {
725
+            $shares[] = $this->createShare($data);
726
+        }
727
+        $cursor->closeCursor();
728
+
729
+        return $shares;
730
+    }
731
+
732
+    /**
733
+     * @inheritdoc
734
+     */
735
+    public function getShareById($id, $recipientId = null) {
736
+        $qb = $this->dbConn->getQueryBuilder();
737
+
738
+        $qb->select('*')
739
+            ->from('share')
740
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
741
+            ->andWhere(
742
+                $qb->expr()->in(
743
+                    'share_type',
744
+                    $qb->createNamedParameter([
745
+                        IShare::TYPE_USER,
746
+                        IShare::TYPE_GROUP,
747
+                        IShare::TYPE_LINK,
748
+                    ], IQueryBuilder::PARAM_INT_ARRAY)
749
+                )
750
+            )
751
+            ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
752
+
753
+        $cursor = $qb->executeQuery();
754
+        $data = $cursor->fetch();
755
+        $cursor->closeCursor();
756
+
757
+        if ($data === false) {
758
+            throw new ShareNotFound();
759
+        }
760
+
761
+        try {
762
+            $share = $this->createShare($data);
763
+        } catch (InvalidShare $e) {
764
+            throw new ShareNotFound();
765
+        }
766
+
767
+        // If the recipient is set for a group share resolve to that user
768
+        if ($recipientId !== null && $share->getShareType() === IShare::TYPE_GROUP) {
769
+            $share = $this->resolveGroupShares([(int)$share->getId() => $share], $recipientId)[0];
770
+        }
771
+
772
+        return $share;
773
+    }
774
+
775
+    /**
776
+     * Get shares for a given path
777
+     *
778
+     * @param \OCP\Files\Node $path
779
+     * @return \OCP\Share\IShare[]
780
+     */
781
+    public function getSharesByPath(Node $path) {
782
+        $qb = $this->dbConn->getQueryBuilder();
783
+
784
+        $cursor = $qb->select('*')
785
+            ->from('share')
786
+            ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
787
+            ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK], IQueryBuilder::PARAM_INT_ARRAY)))
788
+            ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
789
+            ->orderBy('id', 'ASC')
790
+            ->executeQuery();
791
+
792
+        $shares = [];
793
+        while ($data = $cursor->fetch()) {
794
+            $shares[] = $this->createShare($data);
795
+        }
796
+        $cursor->closeCursor();
797
+
798
+        return $shares;
799
+    }
800
+
801
+    /**
802
+     * Returns whether the given database result can be interpreted as
803
+     * a share with accessible file (not trashed, not deleted)
804
+     */
805
+    private function isAccessibleResult($data) {
806
+        // exclude shares leading to deleted file entries
807
+        if ($data['fileid'] === null || $data['path'] === null) {
808
+            return false;
809
+        }
810
+
811
+        // exclude shares leading to trashbin on home storages
812
+        $pathSections = explode('/', $data['path'], 2);
813
+        // FIXME: would not detect rare md5'd home storage case properly
814
+        if ($pathSections[0] !== 'files'
815
+            && (str_starts_with($data['storage_string_id'], 'home::') || str_starts_with($data['storage_string_id'], 'object::user'))) {
816
+            return false;
817
+        } elseif ($pathSections[0] === '__groupfolders'
818
+            && str_starts_with($pathSections[1], 'trash/')
819
+        ) {
820
+            // exclude shares leading to trashbin on group folders storages
821
+            return false;
822
+        }
823
+        return true;
824
+    }
825
+
826
+    /**
827
+     * @inheritdoc
828
+     */
829
+    public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
830
+        /** @var Share[] $shares */
831
+        $shares = [];
832
+
833
+        if ($shareType === IShare::TYPE_USER) {
834
+            //Get shares directly with this user
835
+            $qb = $this->dbConn->getQueryBuilder();
836
+            $qb->select('s.*',
837
+                'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
838
+                'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
839
+                'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
840
+            )
841
+                ->selectAlias('st.id', 'storage_string_id')
842
+                ->from('share', 's')
843
+                ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
844
+                ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
845
+
846
+            // Order by id
847
+            $qb->orderBy('s.id');
848
+
849
+            // Set limit and offset
850
+            if ($limit !== -1) {
851
+                $qb->setMaxResults($limit);
852
+            }
853
+            $qb->setFirstResult($offset);
854
+
855
+            $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)))
856
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
857
+                ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
858
+
859
+            // Filter by node if provided
860
+            if ($node !== null) {
861
+                $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
862
+            }
863
+
864
+            $cursor = $qb->executeQuery();
865
+
866
+            while ($data = $cursor->fetch()) {
867
+                if ($data['fileid'] && $data['path'] === null) {
868
+                    $data['path'] = (string)$data['path'];
869
+                    $data['name'] = (string)$data['name'];
870
+                    $data['checksum'] = (string)$data['checksum'];
871
+                }
872
+                if ($this->isAccessibleResult($data)) {
873
+                    $shares[] = $this->createShare($data);
874
+                }
875
+            }
876
+            $cursor->closeCursor();
877
+        } elseif ($shareType === IShare::TYPE_GROUP) {
878
+            $user = new LazyUser($userId, $this->userManager);
879
+            $allGroups = $this->groupManager->getUserGroupIds($user);
880
+
881
+            /** @var Share[] $shares2 */
882
+            $shares2 = [];
883
+
884
+            $start = 0;
885
+            while (true) {
886
+                $groups = array_slice($allGroups, $start, 1000);
887
+                $start += 1000;
888
+
889
+                if ($groups === []) {
890
+                    break;
891
+                }
892
+
893
+                $qb = $this->dbConn->getQueryBuilder();
894
+                $qb->select('s.*',
895
+                    'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
896
+                    'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
897
+                    'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
898
+                )
899
+                    ->selectAlias('st.id', 'storage_string_id')
900
+                    ->from('share', 's')
901
+                    ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
902
+                    ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
903
+                    ->orderBy('s.id')
904
+                    ->setFirstResult(0);
905
+
906
+                if ($limit !== -1) {
907
+                    $qb->setMaxResults($limit - count($shares));
908
+                }
909
+
910
+                // Filter by node if provided
911
+                if ($node !== null) {
912
+                    $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
913
+                }
914
+
915
+                $groups = array_filter($groups);
916
+
917
+                $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
918
+                    ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
919
+                        $groups,
920
+                        IQueryBuilder::PARAM_STR_ARRAY
921
+                    )))
922
+                    ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
923
+
924
+                $cursor = $qb->executeQuery();
925
+                while ($data = $cursor->fetch()) {
926
+                    if ($offset > 0) {
927
+                        $offset--;
928
+                        continue;
929
+                    }
930
+
931
+                    if ($this->isAccessibleResult($data)) {
932
+                        $share = $this->createShare($data);
933
+                        $shares2[$share->getId()] = $share;
934
+                    }
935
+                }
936
+                $cursor->closeCursor();
937
+            }
938
+
939
+            /*
940 940
 			 * Resolve all group shares to user specific shares
941 941
 			 */
942
-			$shares = $this->resolveGroupShares($shares2, $userId);
943
-		} else {
944
-			throw new BackendError('Invalid backend');
945
-		}
946
-
947
-
948
-		return $shares;
949
-	}
950
-
951
-	/**
952
-	 * Get a share by token
953
-	 *
954
-	 * @param string $token
955
-	 * @return \OCP\Share\IShare
956
-	 * @throws ShareNotFound
957
-	 */
958
-	public function getShareByToken($token) {
959
-		$qb = $this->dbConn->getQueryBuilder();
960
-
961
-		$cursor = $qb->select('*')
962
-			->from('share')
963
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)))
964
-			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
965
-			->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
966
-			->executeQuery();
967
-
968
-		$data = $cursor->fetch();
969
-
970
-		if ($data === false) {
971
-			throw new ShareNotFound();
972
-		}
973
-
974
-		try {
975
-			$share = $this->createShare($data);
976
-		} catch (InvalidShare $e) {
977
-			throw new ShareNotFound();
978
-		}
979
-
980
-		return $share;
981
-	}
982
-
983
-	/**
984
-	 * Create a share object from a database row
985
-	 *
986
-	 * @param mixed[] $data
987
-	 * @return \OCP\Share\IShare
988
-	 * @throws InvalidShare
989
-	 */
990
-	private function createShare($data) {
991
-		$share = new Share($this->rootFolder, $this->userManager);
992
-		$share->setId($data['id'])
993
-			->setShareType((int)$data['share_type'])
994
-			->setPermissions((int)$data['permissions'])
995
-			->setTarget($data['file_target'])
996
-			->setNote((string)$data['note'])
997
-			->setMailSend((bool)$data['mail_send'])
998
-			->setStatus((int)$data['accepted'])
999
-			->setLabel($data['label'] ?? '');
1000
-
1001
-		$shareTime = new \DateTime();
1002
-		$shareTime->setTimestamp((int)$data['stime']);
1003
-		$share->setShareTime($shareTime);
1004
-
1005
-		if ($share->getShareType() === IShare::TYPE_USER) {
1006
-			$share->setSharedWith($data['share_with']);
1007
-			$displayName = $this->userManager->getDisplayName($data['share_with']);
1008
-			if ($displayName !== null) {
1009
-				$share->setSharedWithDisplayName($displayName);
1010
-			}
1011
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1012
-			$share->setSharedWith($data['share_with']);
1013
-			$group = $this->groupManager->get($data['share_with']);
1014
-			if ($group !== null) {
1015
-				$share->setSharedWithDisplayName($group->getDisplayName());
1016
-			}
1017
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
1018
-			$share->setPassword($data['password']);
1019
-			$share->setSendPasswordByTalk((bool)$data['password_by_talk']);
1020
-			$share->setToken($data['token']);
1021
-		}
1022
-
1023
-		$share = $this->updateShareAttributes($share, $data['attributes']);
1024
-
1025
-		$share->setSharedBy($data['uid_initiator']);
1026
-		$share->setShareOwner($data['uid_owner']);
1027
-
1028
-		$share->setNodeId((int)$data['file_source']);
1029
-		$share->setNodeType($data['item_type']);
1030
-
1031
-		if ($data['expiration'] !== null) {
1032
-			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
1033
-			$share->setExpirationDate($expiration);
1034
-		}
1035
-
1036
-		if (isset($data['f_permissions'])) {
1037
-			$entryData = $data;
1038
-			$entryData['permissions'] = $entryData['f_permissions'];
1039
-			$entryData['parent'] = $entryData['f_parent'];
1040
-			$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
1041
-				\OC::$server->getMimeTypeLoader()));
1042
-		}
1043
-
1044
-		$share->setProviderId($this->identifier());
1045
-		$share->setHideDownload((int)$data['hide_download'] === 1);
1046
-		$share->setReminderSent((bool)$data['reminder_sent']);
1047
-
1048
-		return $share;
1049
-	}
1050
-
1051
-	/**
1052
-	 * Update the data from group shares with any per-user modifications
1053
-	 *
1054
-	 * @param array<int, Share> $shareMap shares indexed by share id
1055
-	 * @param $userId
1056
-	 * @return Share[] The updates shares if no update is found for a share return the original
1057
-	 */
1058
-	private function resolveGroupShares($shareMap, $userId) {
1059
-		$qb = $this->dbConn->getQueryBuilder();
1060
-		$query = $qb->select('*')
1061
-			->from('share')
1062
-			->where($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
1063
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1064
-			->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
1065
-
1066
-		// this is called with either all group shares or one group share.
1067
-		// for all shares it's easier to just only search by share_with,
1068
-		// for a single share it's efficient to filter by parent
1069
-		if (count($shareMap) === 1) {
1070
-			$share = reset($shareMap);
1071
-			$query->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
1072
-		}
1073
-
1074
-		$stmt = $query->executeQuery();
1075
-
1076
-		while ($data = $stmt->fetch()) {
1077
-			if (array_key_exists($data['parent'], $shareMap)) {
1078
-				$shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
1079
-				$shareMap[$data['parent']]->setStatus((int)$data['accepted']);
1080
-				$shareMap[$data['parent']]->setTarget($data['file_target']);
1081
-				$shareMap[$data['parent']]->setParent($data['parent']);
1082
-			}
1083
-		}
1084
-
1085
-		return array_values($shareMap);
1086
-	}
1087
-
1088
-	/**
1089
-	 * A user is deleted from the system
1090
-	 * So clean up the relevant shares.
1091
-	 *
1092
-	 * @param string $uid
1093
-	 * @param int $shareType
1094
-	 */
1095
-	public function userDeleted($uid, $shareType) {
1096
-		$qb = $this->dbConn->getQueryBuilder();
1097
-
1098
-		$qb->delete('share');
1099
-
1100
-		if ($shareType === IShare::TYPE_USER) {
1101
-			/*
942
+            $shares = $this->resolveGroupShares($shares2, $userId);
943
+        } else {
944
+            throw new BackendError('Invalid backend');
945
+        }
946
+
947
+
948
+        return $shares;
949
+    }
950
+
951
+    /**
952
+     * Get a share by token
953
+     *
954
+     * @param string $token
955
+     * @return \OCP\Share\IShare
956
+     * @throws ShareNotFound
957
+     */
958
+    public function getShareByToken($token) {
959
+        $qb = $this->dbConn->getQueryBuilder();
960
+
961
+        $cursor = $qb->select('*')
962
+            ->from('share')
963
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)))
964
+            ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
965
+            ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
966
+            ->executeQuery();
967
+
968
+        $data = $cursor->fetch();
969
+
970
+        if ($data === false) {
971
+            throw new ShareNotFound();
972
+        }
973
+
974
+        try {
975
+            $share = $this->createShare($data);
976
+        } catch (InvalidShare $e) {
977
+            throw new ShareNotFound();
978
+        }
979
+
980
+        return $share;
981
+    }
982
+
983
+    /**
984
+     * Create a share object from a database row
985
+     *
986
+     * @param mixed[] $data
987
+     * @return \OCP\Share\IShare
988
+     * @throws InvalidShare
989
+     */
990
+    private function createShare($data) {
991
+        $share = new Share($this->rootFolder, $this->userManager);
992
+        $share->setId($data['id'])
993
+            ->setShareType((int)$data['share_type'])
994
+            ->setPermissions((int)$data['permissions'])
995
+            ->setTarget($data['file_target'])
996
+            ->setNote((string)$data['note'])
997
+            ->setMailSend((bool)$data['mail_send'])
998
+            ->setStatus((int)$data['accepted'])
999
+            ->setLabel($data['label'] ?? '');
1000
+
1001
+        $shareTime = new \DateTime();
1002
+        $shareTime->setTimestamp((int)$data['stime']);
1003
+        $share->setShareTime($shareTime);
1004
+
1005
+        if ($share->getShareType() === IShare::TYPE_USER) {
1006
+            $share->setSharedWith($data['share_with']);
1007
+            $displayName = $this->userManager->getDisplayName($data['share_with']);
1008
+            if ($displayName !== null) {
1009
+                $share->setSharedWithDisplayName($displayName);
1010
+            }
1011
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1012
+            $share->setSharedWith($data['share_with']);
1013
+            $group = $this->groupManager->get($data['share_with']);
1014
+            if ($group !== null) {
1015
+                $share->setSharedWithDisplayName($group->getDisplayName());
1016
+            }
1017
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
1018
+            $share->setPassword($data['password']);
1019
+            $share->setSendPasswordByTalk((bool)$data['password_by_talk']);
1020
+            $share->setToken($data['token']);
1021
+        }
1022
+
1023
+        $share = $this->updateShareAttributes($share, $data['attributes']);
1024
+
1025
+        $share->setSharedBy($data['uid_initiator']);
1026
+        $share->setShareOwner($data['uid_owner']);
1027
+
1028
+        $share->setNodeId((int)$data['file_source']);
1029
+        $share->setNodeType($data['item_type']);
1030
+
1031
+        if ($data['expiration'] !== null) {
1032
+            $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
1033
+            $share->setExpirationDate($expiration);
1034
+        }
1035
+
1036
+        if (isset($data['f_permissions'])) {
1037
+            $entryData = $data;
1038
+            $entryData['permissions'] = $entryData['f_permissions'];
1039
+            $entryData['parent'] = $entryData['f_parent'];
1040
+            $share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
1041
+                \OC::$server->getMimeTypeLoader()));
1042
+        }
1043
+
1044
+        $share->setProviderId($this->identifier());
1045
+        $share->setHideDownload((int)$data['hide_download'] === 1);
1046
+        $share->setReminderSent((bool)$data['reminder_sent']);
1047
+
1048
+        return $share;
1049
+    }
1050
+
1051
+    /**
1052
+     * Update the data from group shares with any per-user modifications
1053
+     *
1054
+     * @param array<int, Share> $shareMap shares indexed by share id
1055
+     * @param $userId
1056
+     * @return Share[] The updates shares if no update is found for a share return the original
1057
+     */
1058
+    private function resolveGroupShares($shareMap, $userId) {
1059
+        $qb = $this->dbConn->getQueryBuilder();
1060
+        $query = $qb->select('*')
1061
+            ->from('share')
1062
+            ->where($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
1063
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1064
+            ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
1065
+
1066
+        // this is called with either all group shares or one group share.
1067
+        // for all shares it's easier to just only search by share_with,
1068
+        // for a single share it's efficient to filter by parent
1069
+        if (count($shareMap) === 1) {
1070
+            $share = reset($shareMap);
1071
+            $query->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
1072
+        }
1073
+
1074
+        $stmt = $query->executeQuery();
1075
+
1076
+        while ($data = $stmt->fetch()) {
1077
+            if (array_key_exists($data['parent'], $shareMap)) {
1078
+                $shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
1079
+                $shareMap[$data['parent']]->setStatus((int)$data['accepted']);
1080
+                $shareMap[$data['parent']]->setTarget($data['file_target']);
1081
+                $shareMap[$data['parent']]->setParent($data['parent']);
1082
+            }
1083
+        }
1084
+
1085
+        return array_values($shareMap);
1086
+    }
1087
+
1088
+    /**
1089
+     * A user is deleted from the system
1090
+     * So clean up the relevant shares.
1091
+     *
1092
+     * @param string $uid
1093
+     * @param int $shareType
1094
+     */
1095
+    public function userDeleted($uid, $shareType) {
1096
+        $qb = $this->dbConn->getQueryBuilder();
1097
+
1098
+        $qb->delete('share');
1099
+
1100
+        if ($shareType === IShare::TYPE_USER) {
1101
+            /*
1102 1102
 			 * Delete all user shares that are owned by this user
1103 1103
 			 * or that are received by this user
1104 1104
 			 */
1105 1105
 
1106
-			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)));
1106
+            $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)));
1107 1107
 
1108
-			$qb->andWhere(
1109
-				$qb->expr()->orX(
1110
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1111
-					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1112
-				)
1113
-			);
1114
-		} elseif ($shareType === IShare::TYPE_GROUP) {
1115
-			/*
1108
+            $qb->andWhere(
1109
+                $qb->expr()->orX(
1110
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1111
+                    $qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1112
+                )
1113
+            );
1114
+        } elseif ($shareType === IShare::TYPE_GROUP) {
1115
+            /*
1116 1116
 			 * Delete all group shares that are owned by this user
1117 1117
 			 * Or special user group shares that are received by this user
1118 1118
 			 */
1119
-			$qb->where(
1120
-				$qb->expr()->andX(
1121
-					$qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_GROUP, IShare::TYPE_USERGROUP], IQueryBuilder::PARAM_INT_ARRAY)),
1122
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
1123
-				)
1124
-			);
1125
-
1126
-			$qb->orWhere(
1127
-				$qb->expr()->andX(
1128
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)),
1129
-					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1130
-				)
1131
-			);
1132
-		} elseif ($shareType === IShare::TYPE_LINK) {
1133
-			/*
1119
+            $qb->where(
1120
+                $qb->expr()->andX(
1121
+                    $qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_GROUP, IShare::TYPE_USERGROUP], IQueryBuilder::PARAM_INT_ARRAY)),
1122
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
1123
+                )
1124
+            );
1125
+
1126
+            $qb->orWhere(
1127
+                $qb->expr()->andX(
1128
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)),
1129
+                    $qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1130
+                )
1131
+            );
1132
+        } elseif ($shareType === IShare::TYPE_LINK) {
1133
+            /*
1134 1134
 			 * Delete all link shares owned by this user.
1135 1135
 			 * And all link shares initiated by this user (until #22327 is in)
1136 1136
 			 */
1137
-			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)));
1138
-
1139
-			$qb->andWhere(
1140
-				$qb->expr()->orX(
1141
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1142
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
1143
-				)
1144
-			);
1145
-		} else {
1146
-			$e = new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType);
1147
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
1148
-			return;
1149
-		}
1150
-
1151
-		$qb->executeStatement();
1152
-	}
1153
-
1154
-	/**
1155
-	 * Delete all shares received by this group. As well as any custom group
1156
-	 * shares for group members.
1157
-	 *
1158
-	 * @param string $gid
1159
-	 */
1160
-	public function groupDeleted($gid) {
1161
-		/*
1137
+            $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)));
1138
+
1139
+            $qb->andWhere(
1140
+                $qb->expr()->orX(
1141
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1142
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
1143
+                )
1144
+            );
1145
+        } else {
1146
+            $e = new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType);
1147
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
1148
+            return;
1149
+        }
1150
+
1151
+        $qb->executeStatement();
1152
+    }
1153
+
1154
+    /**
1155
+     * Delete all shares received by this group. As well as any custom group
1156
+     * shares for group members.
1157
+     *
1158
+     * @param string $gid
1159
+     */
1160
+    public function groupDeleted($gid) {
1161
+        /*
1162 1162
 		 * First delete all custom group shares for group members
1163 1163
 		 */
1164
-		$qb = $this->dbConn->getQueryBuilder();
1165
-		$qb->select('id')
1166
-			->from('share')
1167
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1168
-			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1169
-
1170
-		$cursor = $qb->executeQuery();
1171
-		$ids = [];
1172
-		while ($row = $cursor->fetch()) {
1173
-			$ids[] = (int)$row['id'];
1174
-		}
1175
-		$cursor->closeCursor();
1176
-
1177
-		if (!empty($ids)) {
1178
-			$chunks = array_chunk($ids, 100);
1179
-
1180
-			$qb = $this->dbConn->getQueryBuilder();
1181
-			$qb->delete('share')
1182
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1183
-				->andWhere($qb->expr()->in('parent', $qb->createParameter('parents')));
1184
-
1185
-			foreach ($chunks as $chunk) {
1186
-				$qb->setParameter('parents', $chunk, IQueryBuilder::PARAM_INT_ARRAY);
1187
-				$qb->executeStatement();
1188
-			}
1189
-		}
1190
-
1191
-		/*
1164
+        $qb = $this->dbConn->getQueryBuilder();
1165
+        $qb->select('id')
1166
+            ->from('share')
1167
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1168
+            ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1169
+
1170
+        $cursor = $qb->executeQuery();
1171
+        $ids = [];
1172
+        while ($row = $cursor->fetch()) {
1173
+            $ids[] = (int)$row['id'];
1174
+        }
1175
+        $cursor->closeCursor();
1176
+
1177
+        if (!empty($ids)) {
1178
+            $chunks = array_chunk($ids, 100);
1179
+
1180
+            $qb = $this->dbConn->getQueryBuilder();
1181
+            $qb->delete('share')
1182
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1183
+                ->andWhere($qb->expr()->in('parent', $qb->createParameter('parents')));
1184
+
1185
+            foreach ($chunks as $chunk) {
1186
+                $qb->setParameter('parents', $chunk, IQueryBuilder::PARAM_INT_ARRAY);
1187
+                $qb->executeStatement();
1188
+            }
1189
+        }
1190
+
1191
+        /*
1192 1192
 		 * Now delete all the group shares
1193 1193
 		 */
1194
-		$qb = $this->dbConn->getQueryBuilder();
1195
-		$qb->delete('share')
1196
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1197
-			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1198
-		$qb->executeStatement();
1199
-	}
1200
-
1201
-	/**
1202
-	 * Delete custom group shares to this group for this user
1203
-	 *
1204
-	 * @param string $uid
1205
-	 * @param string $gid
1206
-	 * @return void
1207
-	 */
1208
-	public function userDeletedFromGroup($uid, $gid) {
1209
-		/*
1194
+        $qb = $this->dbConn->getQueryBuilder();
1195
+        $qb->delete('share')
1196
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1197
+            ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1198
+        $qb->executeStatement();
1199
+    }
1200
+
1201
+    /**
1202
+     * Delete custom group shares to this group for this user
1203
+     *
1204
+     * @param string $uid
1205
+     * @param string $gid
1206
+     * @return void
1207
+     */
1208
+    public function userDeletedFromGroup($uid, $gid) {
1209
+        /*
1210 1210
 		 * Get all group shares
1211 1211
 		 */
1212
-		$qb = $this->dbConn->getQueryBuilder();
1213
-		$qb->select('id')
1214
-			->from('share')
1215
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1216
-			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1217
-
1218
-		$cursor = $qb->executeQuery();
1219
-		$ids = [];
1220
-		while ($row = $cursor->fetch()) {
1221
-			$ids[] = (int)$row['id'];
1222
-		}
1223
-		$cursor->closeCursor();
1224
-
1225
-		if (!empty($ids)) {
1226
-			$chunks = array_chunk($ids, 100);
1227
-
1228
-			/*
1212
+        $qb = $this->dbConn->getQueryBuilder();
1213
+        $qb->select('id')
1214
+            ->from('share')
1215
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1216
+            ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1217
+
1218
+        $cursor = $qb->executeQuery();
1219
+        $ids = [];
1220
+        while ($row = $cursor->fetch()) {
1221
+            $ids[] = (int)$row['id'];
1222
+        }
1223
+        $cursor->closeCursor();
1224
+
1225
+        if (!empty($ids)) {
1226
+            $chunks = array_chunk($ids, 100);
1227
+
1228
+            /*
1229 1229
 			 * Delete all special shares with this user for the found group shares
1230 1230
 			 */
1231
-			$qb = $this->dbConn->getQueryBuilder();
1232
-			$qb->delete('share')
1233
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1234
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
1235
-				->andWhere($qb->expr()->in('parent', $qb->createParameter('parents')));
1236
-
1237
-			foreach ($chunks as $chunk) {
1238
-				$qb->setParameter('parents', $chunk, IQueryBuilder::PARAM_INT_ARRAY);
1239
-				$qb->executeStatement();
1240
-			}
1241
-		}
1242
-
1243
-		if ($this->shareManager->shareWithGroupMembersOnly()) {
1244
-			$user = $this->userManager->get($uid);
1245
-			if ($user === null) {
1246
-				return;
1247
-			}
1248
-			$userGroups = $this->groupManager->getUserGroupIds($user);
1249
-			$userGroups = array_diff($userGroups, $this->shareManager->shareWithGroupMembersOnlyExcludeGroupsList());
1250
-
1251
-			// Delete user shares received by the user from users in the group.
1252
-			$userReceivedShares = $this->shareManager->getSharedWith($uid, IShare::TYPE_USER, null, -1);
1253
-			foreach ($userReceivedShares as $share) {
1254
-				$owner = $this->userManager->get($share->getSharedBy());
1255
-				if ($owner === null) {
1256
-					continue;
1257
-				}
1258
-				$ownerGroups = $this->groupManager->getUserGroupIds($owner);
1259
-				$mutualGroups = array_intersect($userGroups, $ownerGroups);
1260
-
1261
-				if (count($mutualGroups) === 0) {
1262
-					$this->shareManager->deleteShare($share);
1263
-				}
1264
-			}
1265
-
1266
-			// Delete user shares from the user to users in the group.
1267
-			$userEmittedShares = $this->shareManager->getSharesBy($uid, IShare::TYPE_USER, null, true, -1);
1268
-			foreach ($userEmittedShares as $share) {
1269
-				$recipient = $this->userManager->get($share->getSharedWith());
1270
-				if ($recipient === null) {
1271
-					continue;
1272
-				}
1273
-				$recipientGroups = $this->groupManager->getUserGroupIds($recipient);
1274
-				$mutualGroups = array_intersect($userGroups, $recipientGroups);
1275
-
1276
-				if (count($mutualGroups) === 0) {
1277
-					$this->shareManager->deleteShare($share);
1278
-				}
1279
-			}
1280
-		}
1281
-	}
1282
-
1283
-	/**
1284
-	 * @inheritdoc
1285
-	 */
1286
-	public function getAccessList($nodes, $currentAccess) {
1287
-		$ids = [];
1288
-		foreach ($nodes as $node) {
1289
-			$ids[] = $node->getId();
1290
-		}
1291
-
1292
-		$qb = $this->dbConn->getQueryBuilder();
1293
-
1294
-		$shareTypes = [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK];
1295
-
1296
-		if ($currentAccess) {
1297
-			$shareTypes[] = IShare::TYPE_USERGROUP;
1298
-		}
1299
-
1300
-		$qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions')
1301
-			->from('share')
1302
-			->where(
1303
-				$qb->expr()->in('share_type', $qb->createNamedParameter($shareTypes, IQueryBuilder::PARAM_INT_ARRAY))
1304
-			)
1305
-			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1306
-			->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
1307
-
1308
-		// Ensure accepted is true for user and usergroup type
1309
-		$qb->andWhere(
1310
-			$qb->expr()->orX(
1311
-				$qb->expr()->andX(
1312
-					$qb->expr()->neq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
1313
-					$qb->expr()->neq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)),
1314
-				),
1315
-				$qb->expr()->eq('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED, IQueryBuilder::PARAM_INT)),
1316
-			),
1317
-		);
1318
-
1319
-		$cursor = $qb->executeQuery();
1320
-
1321
-		$users = [];
1322
-		$link = false;
1323
-		while ($row = $cursor->fetch()) {
1324
-			$type = (int)$row['share_type'];
1325
-			if ($type === IShare::TYPE_USER) {
1326
-				$uid = $row['share_with'];
1327
-				$users[$uid] = $users[$uid] ?? [];
1328
-				$users[$uid][$row['id']] = $row;
1329
-			} elseif ($type === IShare::TYPE_GROUP) {
1330
-				$gid = $row['share_with'];
1331
-				$group = $this->groupManager->get($gid);
1332
-
1333
-				if ($group === null) {
1334
-					continue;
1335
-				}
1336
-
1337
-				$userList = $group->getUsers();
1338
-				foreach ($userList as $user) {
1339
-					$uid = $user->getUID();
1340
-					$users[$uid] = $users[$uid] ?? [];
1341
-					$users[$uid][$row['id']] = $row;
1342
-				}
1343
-			} elseif ($type === IShare::TYPE_LINK) {
1344
-				$link = true;
1345
-			} elseif ($type === IShare::TYPE_USERGROUP && $currentAccess === true) {
1346
-				$uid = $row['share_with'];
1347
-				$users[$uid] = $users[$uid] ?? [];
1348
-				$users[$uid][$row['id']] = $row;
1349
-			}
1350
-		}
1351
-		$cursor->closeCursor();
1352
-
1353
-		if ($currentAccess === true) {
1354
-			$users = array_map([$this, 'filterSharesOfUser'], $users);
1355
-			$users = array_filter($users);
1356
-		} else {
1357
-			$users = array_keys($users);
1358
-		}
1359
-
1360
-		return ['users' => $users, 'public' => $link];
1361
-	}
1362
-
1363
-	/**
1364
-	 * For each user the path with the fewest slashes is returned
1365
-	 * @param array $shares
1366
-	 * @return array
1367
-	 */
1368
-	protected function filterSharesOfUser(array $shares) {
1369
-		// Group shares when the user has a share exception
1370
-		foreach ($shares as $id => $share) {
1371
-			$type = (int)$share['share_type'];
1372
-			$permissions = (int)$share['permissions'];
1373
-
1374
-			if ($type === IShare::TYPE_USERGROUP) {
1375
-				unset($shares[$share['parent']]);
1376
-
1377
-				if ($permissions === 0) {
1378
-					unset($shares[$id]);
1379
-				}
1380
-			}
1381
-		}
1382
-
1383
-		$best = [];
1384
-		$bestDepth = 0;
1385
-		foreach ($shares as $id => $share) {
1386
-			$depth = substr_count(($share['file_target'] ?? ''), '/');
1387
-			if (empty($best) || $depth < $bestDepth) {
1388
-				$bestDepth = $depth;
1389
-				$best = [
1390
-					'node_id' => $share['file_source'],
1391
-					'node_path' => $share['file_target'],
1392
-				];
1393
-			}
1394
-		}
1395
-
1396
-		return $best;
1397
-	}
1398
-
1399
-	/**
1400
-	 * propagate notes to the recipients
1401
-	 *
1402
-	 * @param IShare $share
1403
-	 * @throws \OCP\Files\NotFoundException
1404
-	 */
1405
-	private function propagateNote(IShare $share) {
1406
-		if ($share->getShareType() === IShare::TYPE_USER) {
1407
-			$user = $this->userManager->get($share->getSharedWith());
1408
-			$this->sendNote([$user], $share);
1409
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1410
-			$group = $this->groupManager->get($share->getSharedWith());
1411
-			$groupMembers = $group->getUsers();
1412
-			$this->sendNote($groupMembers, $share);
1413
-		}
1414
-	}
1415
-
1416
-	public function sendMailNotification(IShare $share): bool {
1417
-		try {
1418
-			// Check user
1419
-			$user = $this->userManager->get($share->getSharedWith());
1420
-			if ($user === null) {
1421
-				$this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because user could not be found.', ['app' => 'share']);
1422
-				return false;
1423
-			}
1424
-
1425
-			// Handle user shares
1426
-			if ($share->getShareType() === IShare::TYPE_USER) {
1427
-				// Check email address
1428
-				$emailAddress = $user->getEMailAddress();
1429
-				if ($emailAddress === null || $emailAddress === '') {
1430
-					$this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because email address is not set.', ['app' => 'share']);
1431
-					return false;
1432
-				}
1433
-
1434
-				$userLang = $this->l10nFactory->getUserLanguage($user);
1435
-				$l = $this->l10nFactory->get('lib', $userLang);
1436
-				$this->sendUserShareMail(
1437
-					$l,
1438
-					$share->getNode()->getName(),
1439
-					$this->urlGenerator->linkToRouteAbsolute('files_sharing.Accept.accept', ['shareId' => $share->getFullId()]),
1440
-					$share->getSharedBy(),
1441
-					$emailAddress,
1442
-					$share->getExpirationDate(),
1443
-					$share->getNote()
1444
-				);
1445
-				$this->logger->debug('Sent share notification to ' . $emailAddress . ' for share with ID ' . $share->getId() . '.', ['app' => 'share']);
1446
-				return true;
1447
-			}
1448
-		} catch (\Exception $e) {
1449
-			$this->logger->error('Share notification mail could not be sent.', ['exception' => $e]);
1450
-		}
1451
-
1452
-		return false;
1453
-	}
1454
-
1455
-	/**
1456
-	 * Send mail notifications for the user share type
1457
-	 *
1458
-	 * @param IL10N $l Language of the recipient
1459
-	 * @param string $filename file/folder name
1460
-	 * @param string $link link to the file/folder
1461
-	 * @param string $initiator user ID of share sender
1462
-	 * @param string $shareWith email address of share receiver
1463
-	 * @param \DateTime|null $expiration
1464
-	 * @param string $note
1465
-	 * @throws \Exception
1466
-	 */
1467
-	protected function sendUserShareMail(
1468
-		IL10N $l,
1469
-		$filename,
1470
-		$link,
1471
-		$initiator,
1472
-		$shareWith,
1473
-		?\DateTime $expiration = null,
1474
-		$note = '') {
1475
-		$initiatorUser = $this->userManager->get($initiator);
1476
-		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
1477
-
1478
-		$message = $this->mailer->createMessage();
1479
-
1480
-		$emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [
1481
-			'filename' => $filename,
1482
-			'link' => $link,
1483
-			'initiator' => $initiatorDisplayName,
1484
-			'expiration' => $expiration,
1485
-			'shareWith' => $shareWith,
1486
-		]);
1487
-
1488
-		$emailTemplate->setSubject($l->t('%1$s shared %2$s with you', [$initiatorDisplayName, $filename]));
1489
-		$emailTemplate->addHeader();
1490
-		$emailTemplate->addHeading($l->t('%1$s shared %2$s with you', [$initiatorDisplayName, $filename]), false);
1491
-
1492
-		if ($note !== '') {
1493
-			$emailTemplate->addBodyText(htmlspecialchars($note), $note);
1494
-		}
1495
-
1496
-		$emailTemplate->addBodyButton(
1497
-			$l->t('Open %s', [$filename]),
1498
-			$link
1499
-		);
1500
-
1501
-		$message->setTo([$shareWith]);
1502
-
1503
-		// The "From" contains the sharers name
1504
-		$instanceName = $this->defaults->getName();
1505
-		$senderName = $l->t(
1506
-			'%1$s via %2$s',
1507
-			[
1508
-				$initiatorDisplayName,
1509
-				$instanceName,
1510
-			]
1511
-		);
1512
-		$message->setFrom([\OCP\Util::getDefaultEmailAddress('noreply') => $senderName]);
1513
-
1514
-		// The "Reply-To" is set to the sharer if an mail address is configured
1515
-		// also the default footer contains a "Do not reply" which needs to be adjusted.
1516
-		if ($initiatorUser) {
1517
-			$initiatorEmail = $initiatorUser->getEMailAddress();
1518
-			if ($initiatorEmail !== null) {
1519
-				$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
1520
-				$emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : ''));
1521
-			} else {
1522
-				$emailTemplate->addFooter();
1523
-			}
1524
-		} else {
1525
-			$emailTemplate->addFooter();
1526
-		}
1527
-
1528
-		$message->useTemplate($emailTemplate);
1529
-		$failedRecipients = $this->mailer->send($message);
1530
-		if (!empty($failedRecipients)) {
1531
-			$this->logger->error('Share notification mail could not be sent to: ' . implode(', ', $failedRecipients));
1532
-			return;
1533
-		}
1534
-	}
1535
-
1536
-	/**
1537
-	 * send note by mail
1538
-	 *
1539
-	 * @param array $recipients
1540
-	 * @param IShare $share
1541
-	 * @throws \OCP\Files\NotFoundException
1542
-	 */
1543
-	private function sendNote(array $recipients, IShare $share) {
1544
-		$toListByLanguage = [];
1545
-
1546
-		foreach ($recipients as $recipient) {
1547
-			/** @var IUser $recipient */
1548
-			$email = $recipient->getEMailAddress();
1549
-			if ($email) {
1550
-				$language = $this->l10nFactory->getUserLanguage($recipient);
1551
-				if (!isset($toListByLanguage[$language])) {
1552
-					$toListByLanguage[$language] = [];
1553
-				}
1554
-				$toListByLanguage[$language][$email] = $recipient->getDisplayName();
1555
-			}
1556
-		}
1557
-
1558
-		if (empty($toListByLanguage)) {
1559
-			return;
1560
-		}
1561
-
1562
-		foreach ($toListByLanguage as $l10n => $toList) {
1563
-			$filename = $share->getNode()->getName();
1564
-			$initiator = $share->getSharedBy();
1565
-			$note = $share->getNote();
1566
-
1567
-			$l = $this->l10nFactory->get('lib', $l10n);
1568
-
1569
-			$initiatorUser = $this->userManager->get($initiator);
1570
-			$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
1571
-			$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
1572
-			$plainHeading = $l->t('%1$s shared %2$s with you and wants to add:', [$initiatorDisplayName, $filename]);
1573
-			$htmlHeading = $l->t('%1$s shared %2$s with you and wants to add', [$initiatorDisplayName, $filename]);
1574
-			$message = $this->mailer->createMessage();
1575
-
1576
-			$emailTemplate = $this->mailer->createEMailTemplate('defaultShareProvider.sendNote');
1577
-
1578
-			$emailTemplate->setSubject($l->t('%s added a note to a file shared with you', [$initiatorDisplayName]));
1579
-			$emailTemplate->addHeader();
1580
-			$emailTemplate->addHeading($htmlHeading, $plainHeading);
1581
-			$emailTemplate->addBodyText(htmlspecialchars($note), $note);
1582
-
1583
-			$link = $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $share->getNode()->getId()]);
1584
-			$emailTemplate->addBodyButton(
1585
-				$l->t('Open %s', [$filename]),
1586
-				$link
1587
-			);
1588
-
1589
-
1590
-			// The "From" contains the sharers name
1591
-			$instanceName = $this->defaults->getName();
1592
-			$senderName = $l->t(
1593
-				'%1$s via %2$s',
1594
-				[
1595
-					$initiatorDisplayName,
1596
-					$instanceName
1597
-				]
1598
-			);
1599
-			$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
1600
-			if ($initiatorEmailAddress !== null) {
1601
-				$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
1602
-				$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
1603
-			} else {
1604
-				$emailTemplate->addFooter();
1605
-			}
1606
-
1607
-			if (count($toList) === 1) {
1608
-				$message->setTo($toList);
1609
-			} else {
1610
-				$message->setTo([]);
1611
-				$message->setBcc($toList);
1612
-			}
1613
-			$message->useTemplate($emailTemplate);
1614
-			$this->mailer->send($message);
1615
-		}
1616
-	}
1617
-
1618
-	public function getAllShares(): iterable {
1619
-		$qb = $this->dbConn->getQueryBuilder();
1620
-
1621
-		$qb->select('*')
1622
-			->from('share')
1623
-			->where($qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK], IQueryBuilder::PARAM_INT_ARRAY)));
1624
-
1625
-		$cursor = $qb->executeQuery();
1626
-		while ($data = $cursor->fetch()) {
1627
-			try {
1628
-				$share = $this->createShare($data);
1629
-			} catch (InvalidShare $e) {
1630
-				continue;
1631
-			}
1632
-
1633
-			yield $share;
1634
-		}
1635
-		$cursor->closeCursor();
1636
-	}
1637
-
1638
-	/**
1639
-	 * Load from database format (JSON string) to IAttributes
1640
-	 *
1641
-	 * @return IShare the modified share
1642
-	 */
1643
-	protected function updateShareAttributes(IShare $share, ?string $data): IShare {
1644
-		if ($data !== null && $data !== '') {
1645
-			$attributes = new ShareAttributes();
1646
-			$compressedAttributes = \json_decode($data, true);
1647
-			if ($compressedAttributes === false || $compressedAttributes === null) {
1648
-				return $share;
1649
-			}
1650
-			foreach ($compressedAttributes as $compressedAttribute) {
1651
-				$attributes->setAttribute(
1652
-					$compressedAttribute[0],
1653
-					$compressedAttribute[1],
1654
-					$compressedAttribute[2]
1655
-				);
1656
-			}
1657
-			$share->setAttributes($attributes);
1658
-		}
1659
-
1660
-		return $share;
1661
-	}
1662
-
1663
-	/**
1664
-	 * Format IAttributes to database format (JSON string)
1665
-	 */
1666
-	protected function formatShareAttributes(?IAttributes $attributes): ?string {
1667
-		if ($attributes === null || empty($attributes->toArray())) {
1668
-			return null;
1669
-		}
1670
-
1671
-		$compressedAttributes = [];
1672
-		foreach ($attributes->toArray() as $attribute) {
1673
-			$compressedAttributes[] = [
1674
-				0 => $attribute['scope'],
1675
-				1 => $attribute['key'],
1676
-				2 => $attribute['value']
1677
-			];
1678
-		}
1679
-		return \json_encode($compressedAttributes);
1680
-	}
1231
+            $qb = $this->dbConn->getQueryBuilder();
1232
+            $qb->delete('share')
1233
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1234
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
1235
+                ->andWhere($qb->expr()->in('parent', $qb->createParameter('parents')));
1236
+
1237
+            foreach ($chunks as $chunk) {
1238
+                $qb->setParameter('parents', $chunk, IQueryBuilder::PARAM_INT_ARRAY);
1239
+                $qb->executeStatement();
1240
+            }
1241
+        }
1242
+
1243
+        if ($this->shareManager->shareWithGroupMembersOnly()) {
1244
+            $user = $this->userManager->get($uid);
1245
+            if ($user === null) {
1246
+                return;
1247
+            }
1248
+            $userGroups = $this->groupManager->getUserGroupIds($user);
1249
+            $userGroups = array_diff($userGroups, $this->shareManager->shareWithGroupMembersOnlyExcludeGroupsList());
1250
+
1251
+            // Delete user shares received by the user from users in the group.
1252
+            $userReceivedShares = $this->shareManager->getSharedWith($uid, IShare::TYPE_USER, null, -1);
1253
+            foreach ($userReceivedShares as $share) {
1254
+                $owner = $this->userManager->get($share->getSharedBy());
1255
+                if ($owner === null) {
1256
+                    continue;
1257
+                }
1258
+                $ownerGroups = $this->groupManager->getUserGroupIds($owner);
1259
+                $mutualGroups = array_intersect($userGroups, $ownerGroups);
1260
+
1261
+                if (count($mutualGroups) === 0) {
1262
+                    $this->shareManager->deleteShare($share);
1263
+                }
1264
+            }
1265
+
1266
+            // Delete user shares from the user to users in the group.
1267
+            $userEmittedShares = $this->shareManager->getSharesBy($uid, IShare::TYPE_USER, null, true, -1);
1268
+            foreach ($userEmittedShares as $share) {
1269
+                $recipient = $this->userManager->get($share->getSharedWith());
1270
+                if ($recipient === null) {
1271
+                    continue;
1272
+                }
1273
+                $recipientGroups = $this->groupManager->getUserGroupIds($recipient);
1274
+                $mutualGroups = array_intersect($userGroups, $recipientGroups);
1275
+
1276
+                if (count($mutualGroups) === 0) {
1277
+                    $this->shareManager->deleteShare($share);
1278
+                }
1279
+            }
1280
+        }
1281
+    }
1282
+
1283
+    /**
1284
+     * @inheritdoc
1285
+     */
1286
+    public function getAccessList($nodes, $currentAccess) {
1287
+        $ids = [];
1288
+        foreach ($nodes as $node) {
1289
+            $ids[] = $node->getId();
1290
+        }
1291
+
1292
+        $qb = $this->dbConn->getQueryBuilder();
1293
+
1294
+        $shareTypes = [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK];
1295
+
1296
+        if ($currentAccess) {
1297
+            $shareTypes[] = IShare::TYPE_USERGROUP;
1298
+        }
1299
+
1300
+        $qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions')
1301
+            ->from('share')
1302
+            ->where(
1303
+                $qb->expr()->in('share_type', $qb->createNamedParameter($shareTypes, IQueryBuilder::PARAM_INT_ARRAY))
1304
+            )
1305
+            ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1306
+            ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
1307
+
1308
+        // Ensure accepted is true for user and usergroup type
1309
+        $qb->andWhere(
1310
+            $qb->expr()->orX(
1311
+                $qb->expr()->andX(
1312
+                    $qb->expr()->neq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
1313
+                    $qb->expr()->neq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)),
1314
+                ),
1315
+                $qb->expr()->eq('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED, IQueryBuilder::PARAM_INT)),
1316
+            ),
1317
+        );
1318
+
1319
+        $cursor = $qb->executeQuery();
1320
+
1321
+        $users = [];
1322
+        $link = false;
1323
+        while ($row = $cursor->fetch()) {
1324
+            $type = (int)$row['share_type'];
1325
+            if ($type === IShare::TYPE_USER) {
1326
+                $uid = $row['share_with'];
1327
+                $users[$uid] = $users[$uid] ?? [];
1328
+                $users[$uid][$row['id']] = $row;
1329
+            } elseif ($type === IShare::TYPE_GROUP) {
1330
+                $gid = $row['share_with'];
1331
+                $group = $this->groupManager->get($gid);
1332
+
1333
+                if ($group === null) {
1334
+                    continue;
1335
+                }
1336
+
1337
+                $userList = $group->getUsers();
1338
+                foreach ($userList as $user) {
1339
+                    $uid = $user->getUID();
1340
+                    $users[$uid] = $users[$uid] ?? [];
1341
+                    $users[$uid][$row['id']] = $row;
1342
+                }
1343
+            } elseif ($type === IShare::TYPE_LINK) {
1344
+                $link = true;
1345
+            } elseif ($type === IShare::TYPE_USERGROUP && $currentAccess === true) {
1346
+                $uid = $row['share_with'];
1347
+                $users[$uid] = $users[$uid] ?? [];
1348
+                $users[$uid][$row['id']] = $row;
1349
+            }
1350
+        }
1351
+        $cursor->closeCursor();
1352
+
1353
+        if ($currentAccess === true) {
1354
+            $users = array_map([$this, 'filterSharesOfUser'], $users);
1355
+            $users = array_filter($users);
1356
+        } else {
1357
+            $users = array_keys($users);
1358
+        }
1359
+
1360
+        return ['users' => $users, 'public' => $link];
1361
+    }
1362
+
1363
+    /**
1364
+     * For each user the path with the fewest slashes is returned
1365
+     * @param array $shares
1366
+     * @return array
1367
+     */
1368
+    protected function filterSharesOfUser(array $shares) {
1369
+        // Group shares when the user has a share exception
1370
+        foreach ($shares as $id => $share) {
1371
+            $type = (int)$share['share_type'];
1372
+            $permissions = (int)$share['permissions'];
1373
+
1374
+            if ($type === IShare::TYPE_USERGROUP) {
1375
+                unset($shares[$share['parent']]);
1376
+
1377
+                if ($permissions === 0) {
1378
+                    unset($shares[$id]);
1379
+                }
1380
+            }
1381
+        }
1382
+
1383
+        $best = [];
1384
+        $bestDepth = 0;
1385
+        foreach ($shares as $id => $share) {
1386
+            $depth = substr_count(($share['file_target'] ?? ''), '/');
1387
+            if (empty($best) || $depth < $bestDepth) {
1388
+                $bestDepth = $depth;
1389
+                $best = [
1390
+                    'node_id' => $share['file_source'],
1391
+                    'node_path' => $share['file_target'],
1392
+                ];
1393
+            }
1394
+        }
1395
+
1396
+        return $best;
1397
+    }
1398
+
1399
+    /**
1400
+     * propagate notes to the recipients
1401
+     *
1402
+     * @param IShare $share
1403
+     * @throws \OCP\Files\NotFoundException
1404
+     */
1405
+    private function propagateNote(IShare $share) {
1406
+        if ($share->getShareType() === IShare::TYPE_USER) {
1407
+            $user = $this->userManager->get($share->getSharedWith());
1408
+            $this->sendNote([$user], $share);
1409
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1410
+            $group = $this->groupManager->get($share->getSharedWith());
1411
+            $groupMembers = $group->getUsers();
1412
+            $this->sendNote($groupMembers, $share);
1413
+        }
1414
+    }
1415
+
1416
+    public function sendMailNotification(IShare $share): bool {
1417
+        try {
1418
+            // Check user
1419
+            $user = $this->userManager->get($share->getSharedWith());
1420
+            if ($user === null) {
1421
+                $this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because user could not be found.', ['app' => 'share']);
1422
+                return false;
1423
+            }
1424
+
1425
+            // Handle user shares
1426
+            if ($share->getShareType() === IShare::TYPE_USER) {
1427
+                // Check email address
1428
+                $emailAddress = $user->getEMailAddress();
1429
+                if ($emailAddress === null || $emailAddress === '') {
1430
+                    $this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because email address is not set.', ['app' => 'share']);
1431
+                    return false;
1432
+                }
1433
+
1434
+                $userLang = $this->l10nFactory->getUserLanguage($user);
1435
+                $l = $this->l10nFactory->get('lib', $userLang);
1436
+                $this->sendUserShareMail(
1437
+                    $l,
1438
+                    $share->getNode()->getName(),
1439
+                    $this->urlGenerator->linkToRouteAbsolute('files_sharing.Accept.accept', ['shareId' => $share->getFullId()]),
1440
+                    $share->getSharedBy(),
1441
+                    $emailAddress,
1442
+                    $share->getExpirationDate(),
1443
+                    $share->getNote()
1444
+                );
1445
+                $this->logger->debug('Sent share notification to ' . $emailAddress . ' for share with ID ' . $share->getId() . '.', ['app' => 'share']);
1446
+                return true;
1447
+            }
1448
+        } catch (\Exception $e) {
1449
+            $this->logger->error('Share notification mail could not be sent.', ['exception' => $e]);
1450
+        }
1451
+
1452
+        return false;
1453
+    }
1454
+
1455
+    /**
1456
+     * Send mail notifications for the user share type
1457
+     *
1458
+     * @param IL10N $l Language of the recipient
1459
+     * @param string $filename file/folder name
1460
+     * @param string $link link to the file/folder
1461
+     * @param string $initiator user ID of share sender
1462
+     * @param string $shareWith email address of share receiver
1463
+     * @param \DateTime|null $expiration
1464
+     * @param string $note
1465
+     * @throws \Exception
1466
+     */
1467
+    protected function sendUserShareMail(
1468
+        IL10N $l,
1469
+        $filename,
1470
+        $link,
1471
+        $initiator,
1472
+        $shareWith,
1473
+        ?\DateTime $expiration = null,
1474
+        $note = '') {
1475
+        $initiatorUser = $this->userManager->get($initiator);
1476
+        $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
1477
+
1478
+        $message = $this->mailer->createMessage();
1479
+
1480
+        $emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [
1481
+            'filename' => $filename,
1482
+            'link' => $link,
1483
+            'initiator' => $initiatorDisplayName,
1484
+            'expiration' => $expiration,
1485
+            'shareWith' => $shareWith,
1486
+        ]);
1487
+
1488
+        $emailTemplate->setSubject($l->t('%1$s shared %2$s with you', [$initiatorDisplayName, $filename]));
1489
+        $emailTemplate->addHeader();
1490
+        $emailTemplate->addHeading($l->t('%1$s shared %2$s with you', [$initiatorDisplayName, $filename]), false);
1491
+
1492
+        if ($note !== '') {
1493
+            $emailTemplate->addBodyText(htmlspecialchars($note), $note);
1494
+        }
1495
+
1496
+        $emailTemplate->addBodyButton(
1497
+            $l->t('Open %s', [$filename]),
1498
+            $link
1499
+        );
1500
+
1501
+        $message->setTo([$shareWith]);
1502
+
1503
+        // The "From" contains the sharers name
1504
+        $instanceName = $this->defaults->getName();
1505
+        $senderName = $l->t(
1506
+            '%1$s via %2$s',
1507
+            [
1508
+                $initiatorDisplayName,
1509
+                $instanceName,
1510
+            ]
1511
+        );
1512
+        $message->setFrom([\OCP\Util::getDefaultEmailAddress('noreply') => $senderName]);
1513
+
1514
+        // The "Reply-To" is set to the sharer if an mail address is configured
1515
+        // also the default footer contains a "Do not reply" which needs to be adjusted.
1516
+        if ($initiatorUser) {
1517
+            $initiatorEmail = $initiatorUser->getEMailAddress();
1518
+            if ($initiatorEmail !== null) {
1519
+                $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
1520
+                $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : ''));
1521
+            } else {
1522
+                $emailTemplate->addFooter();
1523
+            }
1524
+        } else {
1525
+            $emailTemplate->addFooter();
1526
+        }
1527
+
1528
+        $message->useTemplate($emailTemplate);
1529
+        $failedRecipients = $this->mailer->send($message);
1530
+        if (!empty($failedRecipients)) {
1531
+            $this->logger->error('Share notification mail could not be sent to: ' . implode(', ', $failedRecipients));
1532
+            return;
1533
+        }
1534
+    }
1535
+
1536
+    /**
1537
+     * send note by mail
1538
+     *
1539
+     * @param array $recipients
1540
+     * @param IShare $share
1541
+     * @throws \OCP\Files\NotFoundException
1542
+     */
1543
+    private function sendNote(array $recipients, IShare $share) {
1544
+        $toListByLanguage = [];
1545
+
1546
+        foreach ($recipients as $recipient) {
1547
+            /** @var IUser $recipient */
1548
+            $email = $recipient->getEMailAddress();
1549
+            if ($email) {
1550
+                $language = $this->l10nFactory->getUserLanguage($recipient);
1551
+                if (!isset($toListByLanguage[$language])) {
1552
+                    $toListByLanguage[$language] = [];
1553
+                }
1554
+                $toListByLanguage[$language][$email] = $recipient->getDisplayName();
1555
+            }
1556
+        }
1557
+
1558
+        if (empty($toListByLanguage)) {
1559
+            return;
1560
+        }
1561
+
1562
+        foreach ($toListByLanguage as $l10n => $toList) {
1563
+            $filename = $share->getNode()->getName();
1564
+            $initiator = $share->getSharedBy();
1565
+            $note = $share->getNote();
1566
+
1567
+            $l = $this->l10nFactory->get('lib', $l10n);
1568
+
1569
+            $initiatorUser = $this->userManager->get($initiator);
1570
+            $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
1571
+            $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
1572
+            $plainHeading = $l->t('%1$s shared %2$s with you and wants to add:', [$initiatorDisplayName, $filename]);
1573
+            $htmlHeading = $l->t('%1$s shared %2$s with you and wants to add', [$initiatorDisplayName, $filename]);
1574
+            $message = $this->mailer->createMessage();
1575
+
1576
+            $emailTemplate = $this->mailer->createEMailTemplate('defaultShareProvider.sendNote');
1577
+
1578
+            $emailTemplate->setSubject($l->t('%s added a note to a file shared with you', [$initiatorDisplayName]));
1579
+            $emailTemplate->addHeader();
1580
+            $emailTemplate->addHeading($htmlHeading, $plainHeading);
1581
+            $emailTemplate->addBodyText(htmlspecialchars($note), $note);
1582
+
1583
+            $link = $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $share->getNode()->getId()]);
1584
+            $emailTemplate->addBodyButton(
1585
+                $l->t('Open %s', [$filename]),
1586
+                $link
1587
+            );
1588
+
1589
+
1590
+            // The "From" contains the sharers name
1591
+            $instanceName = $this->defaults->getName();
1592
+            $senderName = $l->t(
1593
+                '%1$s via %2$s',
1594
+                [
1595
+                    $initiatorDisplayName,
1596
+                    $instanceName
1597
+                ]
1598
+            );
1599
+            $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
1600
+            if ($initiatorEmailAddress !== null) {
1601
+                $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
1602
+                $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
1603
+            } else {
1604
+                $emailTemplate->addFooter();
1605
+            }
1606
+
1607
+            if (count($toList) === 1) {
1608
+                $message->setTo($toList);
1609
+            } else {
1610
+                $message->setTo([]);
1611
+                $message->setBcc($toList);
1612
+            }
1613
+            $message->useTemplate($emailTemplate);
1614
+            $this->mailer->send($message);
1615
+        }
1616
+    }
1617
+
1618
+    public function getAllShares(): iterable {
1619
+        $qb = $this->dbConn->getQueryBuilder();
1620
+
1621
+        $qb->select('*')
1622
+            ->from('share')
1623
+            ->where($qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK], IQueryBuilder::PARAM_INT_ARRAY)));
1624
+
1625
+        $cursor = $qb->executeQuery();
1626
+        while ($data = $cursor->fetch()) {
1627
+            try {
1628
+                $share = $this->createShare($data);
1629
+            } catch (InvalidShare $e) {
1630
+                continue;
1631
+            }
1632
+
1633
+            yield $share;
1634
+        }
1635
+        $cursor->closeCursor();
1636
+    }
1637
+
1638
+    /**
1639
+     * Load from database format (JSON string) to IAttributes
1640
+     *
1641
+     * @return IShare the modified share
1642
+     */
1643
+    protected function updateShareAttributes(IShare $share, ?string $data): IShare {
1644
+        if ($data !== null && $data !== '') {
1645
+            $attributes = new ShareAttributes();
1646
+            $compressedAttributes = \json_decode($data, true);
1647
+            if ($compressedAttributes === false || $compressedAttributes === null) {
1648
+                return $share;
1649
+            }
1650
+            foreach ($compressedAttributes as $compressedAttribute) {
1651
+                $attributes->setAttribute(
1652
+                    $compressedAttribute[0],
1653
+                    $compressedAttribute[1],
1654
+                    $compressedAttribute[2]
1655
+                );
1656
+            }
1657
+            $share->setAttributes($attributes);
1658
+        }
1659
+
1660
+        return $share;
1661
+    }
1662
+
1663
+    /**
1664
+     * Format IAttributes to database format (JSON string)
1665
+     */
1666
+    protected function formatShareAttributes(?IAttributes $attributes): ?string {
1667
+        if ($attributes === null || empty($attributes->toArray())) {
1668
+            return null;
1669
+        }
1670
+
1671
+        $compressedAttributes = [];
1672
+        foreach ($attributes->toArray() as $attribute) {
1673
+            $compressedAttributes[] = [
1674
+                0 => $attribute['scope'],
1675
+                1 => $attribute['key'],
1676
+                2 => $attribute['value']
1677
+            ];
1678
+        }
1679
+        return \json_encode($compressedAttributes);
1680
+    }
1681 1681
 }
Please login to merge, or discard this patch.
Spacing   +34 added lines, -34 removed lines patch added patch discarded remove patch
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
 
178 178
 		// Update mandatory data
179 179
 		$id = $qb->getLastInsertId();
180
-		$share->setId((string)$id);
180
+		$share->setId((string) $id);
181 181
 		$share->setProviderId($this->identifier());
182 182
 
183 183
 		$share->setShareTime(\DateTime::createFromImmutable($shareTime));
@@ -308,7 +308,7 @@  discard block
 block discarded – undo
308 308
 			$user = $this->userManager->get($recipient);
309 309
 
310 310
 			if (is_null($group)) {
311
-				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
311
+				throw new ProviderException('Group "'.$share->getSharedWith().'" does not exist');
312 312
 			}
313 313
 
314 314
 			if (!$group->inGroup($user)) {
@@ -421,7 +421,7 @@  discard block
 block discarded – undo
421 421
 			$user = $this->userManager->get($recipient);
422 422
 
423 423
 			if (is_null($group)) {
424
-				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
424
+				throw new ProviderException('Group "'.$share->getSharedWith().'" does not exist');
425 425
 			}
426 426
 
427 427
 			if (!$group->inGroup($user)) {
@@ -482,7 +482,7 @@  discard block
 block discarded – undo
482 482
 			$shareFolder = $this->config->getUserValue($recipient, Application::APP_ID, 'share_folder', $shareFolder);
483 483
 		}
484 484
 
485
-		$target = $shareFolder . '/' . $share->getNode()->getName();
485
+		$target = $shareFolder.'/'.$share->getNode()->getName();
486 486
 		$target = \OC\Files\Filesystem::normalizePath($target);
487 487
 
488 488
 		$qb = $this->dbConn->getQueryBuilder();
@@ -643,10 +643,10 @@  discard block
 block discarded – undo
643 643
 		}
644 644
 
645 645
 		// todo? maybe get these from the oc_mounts table
646
-		$childMountNodes = array_filter($node->getDirectoryListing(), function (Node $node): bool {
646
+		$childMountNodes = array_filter($node->getDirectoryListing(), function(Node $node): bool {
647 647
 			return $node->getInternalPath() === '';
648 648
 		});
649
-		$childMountRootIds = array_map(function (Node $node): int {
649
+		$childMountRootIds = array_map(function(Node $node): int {
650 650
 			return $node->getId();
651 651
 		}, $childMountNodes);
652 652
 
@@ -766,7 +766,7 @@  discard block
 block discarded – undo
766 766
 
767 767
 		// If the recipient is set for a group share resolve to that user
768 768
 		if ($recipientId !== null && $share->getShareType() === IShare::TYPE_GROUP) {
769
-			$share = $this->resolveGroupShares([(int)$share->getId() => $share], $recipientId)[0];
769
+			$share = $this->resolveGroupShares([(int) $share->getId() => $share], $recipientId)[0];
770 770
 		}
771 771
 
772 772
 		return $share;
@@ -865,9 +865,9 @@  discard block
 block discarded – undo
865 865
 
866 866
 			while ($data = $cursor->fetch()) {
867 867
 				if ($data['fileid'] && $data['path'] === null) {
868
-					$data['path'] = (string)$data['path'];
869
-					$data['name'] = (string)$data['name'];
870
-					$data['checksum'] = (string)$data['checksum'];
868
+					$data['path'] = (string) $data['path'];
869
+					$data['name'] = (string) $data['name'];
870
+					$data['checksum'] = (string) $data['checksum'];
871 871
 				}
872 872
 				if ($this->isAccessibleResult($data)) {
873 873
 					$shares[] = $this->createShare($data);
@@ -990,16 +990,16 @@  discard block
 block discarded – undo
990 990
 	private function createShare($data) {
991 991
 		$share = new Share($this->rootFolder, $this->userManager);
992 992
 		$share->setId($data['id'])
993
-			->setShareType((int)$data['share_type'])
994
-			->setPermissions((int)$data['permissions'])
993
+			->setShareType((int) $data['share_type'])
994
+			->setPermissions((int) $data['permissions'])
995 995
 			->setTarget($data['file_target'])
996
-			->setNote((string)$data['note'])
997
-			->setMailSend((bool)$data['mail_send'])
998
-			->setStatus((int)$data['accepted'])
996
+			->setNote((string) $data['note'])
997
+			->setMailSend((bool) $data['mail_send'])
998
+			->setStatus((int) $data['accepted'])
999 999
 			->setLabel($data['label'] ?? '');
1000 1000
 
1001 1001
 		$shareTime = new \DateTime();
1002
-		$shareTime->setTimestamp((int)$data['stime']);
1002
+		$shareTime->setTimestamp((int) $data['stime']);
1003 1003
 		$share->setShareTime($shareTime);
1004 1004
 
1005 1005
 		if ($share->getShareType() === IShare::TYPE_USER) {
@@ -1016,7 +1016,7 @@  discard block
 block discarded – undo
1016 1016
 			}
1017 1017
 		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
1018 1018
 			$share->setPassword($data['password']);
1019
-			$share->setSendPasswordByTalk((bool)$data['password_by_talk']);
1019
+			$share->setSendPasswordByTalk((bool) $data['password_by_talk']);
1020 1020
 			$share->setToken($data['token']);
1021 1021
 		}
1022 1022
 
@@ -1025,7 +1025,7 @@  discard block
 block discarded – undo
1025 1025
 		$share->setSharedBy($data['uid_initiator']);
1026 1026
 		$share->setShareOwner($data['uid_owner']);
1027 1027
 
1028
-		$share->setNodeId((int)$data['file_source']);
1028
+		$share->setNodeId((int) $data['file_source']);
1029 1029
 		$share->setNodeType($data['item_type']);
1030 1030
 
1031 1031
 		if ($data['expiration'] !== null) {
@@ -1042,8 +1042,8 @@  discard block
 block discarded – undo
1042 1042
 		}
1043 1043
 
1044 1044
 		$share->setProviderId($this->identifier());
1045
-		$share->setHideDownload((int)$data['hide_download'] === 1);
1046
-		$share->setReminderSent((bool)$data['reminder_sent']);
1045
+		$share->setHideDownload((int) $data['hide_download'] === 1);
1046
+		$share->setReminderSent((bool) $data['reminder_sent']);
1047 1047
 
1048 1048
 		return $share;
1049 1049
 	}
@@ -1075,8 +1075,8 @@  discard block
 block discarded – undo
1075 1075
 
1076 1076
 		while ($data = $stmt->fetch()) {
1077 1077
 			if (array_key_exists($data['parent'], $shareMap)) {
1078
-				$shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
1079
-				$shareMap[$data['parent']]->setStatus((int)$data['accepted']);
1078
+				$shareMap[$data['parent']]->setPermissions((int) $data['permissions']);
1079
+				$shareMap[$data['parent']]->setStatus((int) $data['accepted']);
1080 1080
 				$shareMap[$data['parent']]->setTarget($data['file_target']);
1081 1081
 				$shareMap[$data['parent']]->setParent($data['parent']);
1082 1082
 			}
@@ -1143,7 +1143,7 @@  discard block
 block discarded – undo
1143 1143
 				)
1144 1144
 			);
1145 1145
 		} else {
1146
-			$e = new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType);
1146
+			$e = new \InvalidArgumentException('Default share provider tried to delete all shares for type: '.$shareType);
1147 1147
 			$this->logger->error($e->getMessage(), ['exception' => $e]);
1148 1148
 			return;
1149 1149
 		}
@@ -1170,7 +1170,7 @@  discard block
 block discarded – undo
1170 1170
 		$cursor = $qb->executeQuery();
1171 1171
 		$ids = [];
1172 1172
 		while ($row = $cursor->fetch()) {
1173
-			$ids[] = (int)$row['id'];
1173
+			$ids[] = (int) $row['id'];
1174 1174
 		}
1175 1175
 		$cursor->closeCursor();
1176 1176
 
@@ -1218,7 +1218,7 @@  discard block
 block discarded – undo
1218 1218
 		$cursor = $qb->executeQuery();
1219 1219
 		$ids = [];
1220 1220
 		while ($row = $cursor->fetch()) {
1221
-			$ids[] = (int)$row['id'];
1221
+			$ids[] = (int) $row['id'];
1222 1222
 		}
1223 1223
 		$cursor->closeCursor();
1224 1224
 
@@ -1321,7 +1321,7 @@  discard block
 block discarded – undo
1321 1321
 		$users = [];
1322 1322
 		$link = false;
1323 1323
 		while ($row = $cursor->fetch()) {
1324
-			$type = (int)$row['share_type'];
1324
+			$type = (int) $row['share_type'];
1325 1325
 			if ($type === IShare::TYPE_USER) {
1326 1326
 				$uid = $row['share_with'];
1327 1327
 				$users[$uid] = $users[$uid] ?? [];
@@ -1368,8 +1368,8 @@  discard block
 block discarded – undo
1368 1368
 	protected function filterSharesOfUser(array $shares) {
1369 1369
 		// Group shares when the user has a share exception
1370 1370
 		foreach ($shares as $id => $share) {
1371
-			$type = (int)$share['share_type'];
1372
-			$permissions = (int)$share['permissions'];
1371
+			$type = (int) $share['share_type'];
1372
+			$permissions = (int) $share['permissions'];
1373 1373
 
1374 1374
 			if ($type === IShare::TYPE_USERGROUP) {
1375 1375
 				unset($shares[$share['parent']]);
@@ -1418,7 +1418,7 @@  discard block
 block discarded – undo
1418 1418
 			// Check user
1419 1419
 			$user = $this->userManager->get($share->getSharedWith());
1420 1420
 			if ($user === null) {
1421
-				$this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because user could not be found.', ['app' => 'share']);
1421
+				$this->logger->debug('Share notification not sent to '.$share->getSharedWith().' because user could not be found.', ['app' => 'share']);
1422 1422
 				return false;
1423 1423
 			}
1424 1424
 
@@ -1427,7 +1427,7 @@  discard block
 block discarded – undo
1427 1427
 				// Check email address
1428 1428
 				$emailAddress = $user->getEMailAddress();
1429 1429
 				if ($emailAddress === null || $emailAddress === '') {
1430
-					$this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because email address is not set.', ['app' => 'share']);
1430
+					$this->logger->debug('Share notification not sent to '.$share->getSharedWith().' because email address is not set.', ['app' => 'share']);
1431 1431
 					return false;
1432 1432
 				}
1433 1433
 
@@ -1442,7 +1442,7 @@  discard block
 block discarded – undo
1442 1442
 					$share->getExpirationDate(),
1443 1443
 					$share->getNote()
1444 1444
 				);
1445
-				$this->logger->debug('Sent share notification to ' . $emailAddress . ' for share with ID ' . $share->getId() . '.', ['app' => 'share']);
1445
+				$this->logger->debug('Sent share notification to '.$emailAddress.' for share with ID '.$share->getId().'.', ['app' => 'share']);
1446 1446
 				return true;
1447 1447
 			}
1448 1448
 		} catch (\Exception $e) {
@@ -1517,7 +1517,7 @@  discard block
 block discarded – undo
1517 1517
 			$initiatorEmail = $initiatorUser->getEMailAddress();
1518 1518
 			if ($initiatorEmail !== null) {
1519 1519
 				$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
1520
-				$emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : ''));
1520
+				$emailTemplate->addFooter($instanceName.($this->defaults->getSlogan() !== '' ? ' - '.$this->defaults->getSlogan() : ''));
1521 1521
 			} else {
1522 1522
 				$emailTemplate->addFooter();
1523 1523
 			}
@@ -1528,7 +1528,7 @@  discard block
 block discarded – undo
1528 1528
 		$message->useTemplate($emailTemplate);
1529 1529
 		$failedRecipients = $this->mailer->send($message);
1530 1530
 		if (!empty($failedRecipients)) {
1531
-			$this->logger->error('Share notification mail could not be sent to: ' . implode(', ', $failedRecipients));
1531
+			$this->logger->error('Share notification mail could not be sent to: '.implode(', ', $failedRecipients));
1532 1532
 			return;
1533 1533
 		}
1534 1534
 	}
@@ -1599,7 +1599,7 @@  discard block
 block discarded – undo
1599 1599
 			$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
1600 1600
 			if ($initiatorEmailAddress !== null) {
1601 1601
 				$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
1602
-				$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
1602
+				$emailTemplate->addFooter($instanceName.' - '.$this->defaults->getSlogan());
1603 1603
 			} else {
1604 1604
 				$emailTemplate->addFooter();
1605 1605
 			}
Please login to merge, or discard this patch.