Completed
Push — master ( 453450...6f0537 )
by
unknown
37:16
created
apps/files/lib/BackgroundJob/ScanFiles.php 1 patch
Indentation   +116 added lines, -116 removed lines patch added patch discarded remove patch
@@ -24,120 +24,120 @@
 block discarded – undo
24 24
  * @package OCA\Files\BackgroundJob
25 25
  */
26 26
 class ScanFiles extends TimedJob {
27
-	/** Amount of users that should get scanned per execution */
28
-	public const USERS_PER_SESSION = 500;
29
-
30
-	public function __construct(
31
-		private IConfig $config,
32
-		private IEventDispatcher $dispatcher,
33
-		private LoggerInterface $logger,
34
-		private IDBConnection $connection,
35
-		ITimeFactory $time,
36
-	) {
37
-		parent::__construct($time);
38
-		// Run once per 10 minutes
39
-		$this->setInterval(60 * 10);
40
-	}
41
-
42
-	protected function runScanner(string $user): void {
43
-		try {
44
-			$scanner = new Scanner(
45
-				$user,
46
-				null,
47
-				$this->dispatcher,
48
-				$this->logger
49
-			);
50
-			$scanner->backgroundScan('');
51
-		} catch (\Exception $e) {
52
-			$this->logger->error($e->getMessage(), ['exception' => $e, 'app' => 'files']);
53
-		}
54
-		\OC_Util::tearDownFS();
55
-	}
56
-
57
-	/**
58
-	 * Find a storage which have unindexed files and return a user with access to the storage
59
-	 *
60
-	 * @return string|false
61
-	 */
62
-	private function getUserToScan() {
63
-		if ($this->connection->getShardDefinition('filecache')) {
64
-			// for sharded filecache, the "LIMIT" from the normal query doesn't work
65
-
66
-			// first we try it with a "LEFT JOIN" on mounts, this is fast, but might return a storage that isn't mounted.
67
-			// we also ask for up to 10 results from different storages to increase the odds of finding a result that is mounted
68
-			$query = $this->connection->getQueryBuilder();
69
-			$query->select('m.user_id')
70
-				->from('filecache', 'f')
71
-				->leftJoin('f', 'mounts', 'm', $query->expr()->eq('m.storage_id', 'f.storage'))
72
-				->where($query->expr()->eq('f.size', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
73
-				->andWhere($query->expr()->gt('f.parent', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
74
-				->setMaxResults(10)
75
-				->groupBy('f.storage')
76
-				->runAcrossAllShards();
77
-
78
-			$result = $query->executeQuery();
79
-			while ($res = $result->fetchAssociative()) {
80
-				if ($res['user_id']) {
81
-					return $res['user_id'];
82
-				}
83
-			}
84
-
85
-			// as a fallback, we try a slower approach where we find all mounted storages first
86
-			// this is essentially doing the inner join manually
87
-			$storages = $this->getAllMountedStorages();
88
-
89
-			$query = $this->connection->getQueryBuilder();
90
-			$query->select('m.user_id')
91
-				->from('filecache', 'f')
92
-				->leftJoin('f', 'mounts', 'm', $query->expr()->eq('m.storage_id', 'f.storage'))
93
-				->where($query->expr()->eq('f.size', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
94
-				->andWhere($query->expr()->gt('f.parent', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
95
-				->andWhere($query->expr()->in('f.storage', $query->createNamedParameter($storages, IQueryBuilder::PARAM_INT_ARRAY)))
96
-				->setMaxResults(1)
97
-				->runAcrossAllShards();
98
-			return $query->executeQuery()->fetchOne();
99
-		} else {
100
-			$query = $this->connection->getQueryBuilder();
101
-			$query->select('m.user_id')
102
-				->from('filecache', 'f')
103
-				->innerJoin('f', 'mounts', 'm', $query->expr()->eq('m.storage_id', 'f.storage'))
104
-				->where($query->expr()->eq('f.size', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
105
-				->andWhere($query->expr()->gt('f.parent', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
106
-				->setMaxResults(1)
107
-				->runAcrossAllShards();
108
-
109
-			return $query->executeQuery()->fetchOne();
110
-		}
111
-	}
112
-
113
-	private function getAllMountedStorages(): array {
114
-		$query = $this->connection->getQueryBuilder();
115
-		$query->selectDistinct('storage_id')
116
-			->from('mounts');
117
-		return $query->executeQuery()->fetchFirstColumn();
118
-	}
119
-
120
-	/**
121
-	 * @param $argument
122
-	 * @throws \Exception
123
-	 */
124
-	protected function run($argument) {
125
-		if ($this->config->getSystemValueBool('files_no_background_scan', false)) {
126
-			return;
127
-		}
128
-
129
-		$usersScanned = 0;
130
-		$lastUser = '';
131
-		$user = $this->getUserToScan();
132
-		while ($user && $usersScanned < self::USERS_PER_SESSION && $lastUser !== $user) {
133
-			$this->runScanner($user);
134
-			$lastUser = $user;
135
-			$user = $this->getUserToScan();
136
-			$usersScanned += 1;
137
-		}
138
-
139
-		if ($lastUser === $user) {
140
-			$this->logger->warning("User $user still has unscanned files after running background scan, background scan might be stopped prematurely");
141
-		}
142
-	}
27
+    /** Amount of users that should get scanned per execution */
28
+    public const USERS_PER_SESSION = 500;
29
+
30
+    public function __construct(
31
+        private IConfig $config,
32
+        private IEventDispatcher $dispatcher,
33
+        private LoggerInterface $logger,
34
+        private IDBConnection $connection,
35
+        ITimeFactory $time,
36
+    ) {
37
+        parent::__construct($time);
38
+        // Run once per 10 minutes
39
+        $this->setInterval(60 * 10);
40
+    }
41
+
42
+    protected function runScanner(string $user): void {
43
+        try {
44
+            $scanner = new Scanner(
45
+                $user,
46
+                null,
47
+                $this->dispatcher,
48
+                $this->logger
49
+            );
50
+            $scanner->backgroundScan('');
51
+        } catch (\Exception $e) {
52
+            $this->logger->error($e->getMessage(), ['exception' => $e, 'app' => 'files']);
53
+        }
54
+        \OC_Util::tearDownFS();
55
+    }
56
+
57
+    /**
58
+     * Find a storage which have unindexed files and return a user with access to the storage
59
+     *
60
+     * @return string|false
61
+     */
62
+    private function getUserToScan() {
63
+        if ($this->connection->getShardDefinition('filecache')) {
64
+            // for sharded filecache, the "LIMIT" from the normal query doesn't work
65
+
66
+            // first we try it with a "LEFT JOIN" on mounts, this is fast, but might return a storage that isn't mounted.
67
+            // we also ask for up to 10 results from different storages to increase the odds of finding a result that is mounted
68
+            $query = $this->connection->getQueryBuilder();
69
+            $query->select('m.user_id')
70
+                ->from('filecache', 'f')
71
+                ->leftJoin('f', 'mounts', 'm', $query->expr()->eq('m.storage_id', 'f.storage'))
72
+                ->where($query->expr()->eq('f.size', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
73
+                ->andWhere($query->expr()->gt('f.parent', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
74
+                ->setMaxResults(10)
75
+                ->groupBy('f.storage')
76
+                ->runAcrossAllShards();
77
+
78
+            $result = $query->executeQuery();
79
+            while ($res = $result->fetchAssociative()) {
80
+                if ($res['user_id']) {
81
+                    return $res['user_id'];
82
+                }
83
+            }
84
+
85
+            // as a fallback, we try a slower approach where we find all mounted storages first
86
+            // this is essentially doing the inner join manually
87
+            $storages = $this->getAllMountedStorages();
88
+
89
+            $query = $this->connection->getQueryBuilder();
90
+            $query->select('m.user_id')
91
+                ->from('filecache', 'f')
92
+                ->leftJoin('f', 'mounts', 'm', $query->expr()->eq('m.storage_id', 'f.storage'))
93
+                ->where($query->expr()->eq('f.size', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
94
+                ->andWhere($query->expr()->gt('f.parent', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
95
+                ->andWhere($query->expr()->in('f.storage', $query->createNamedParameter($storages, IQueryBuilder::PARAM_INT_ARRAY)))
96
+                ->setMaxResults(1)
97
+                ->runAcrossAllShards();
98
+            return $query->executeQuery()->fetchOne();
99
+        } else {
100
+            $query = $this->connection->getQueryBuilder();
101
+            $query->select('m.user_id')
102
+                ->from('filecache', 'f')
103
+                ->innerJoin('f', 'mounts', 'm', $query->expr()->eq('m.storage_id', 'f.storage'))
104
+                ->where($query->expr()->eq('f.size', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
105
+                ->andWhere($query->expr()->gt('f.parent', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
106
+                ->setMaxResults(1)
107
+                ->runAcrossAllShards();
108
+
109
+            return $query->executeQuery()->fetchOne();
110
+        }
111
+    }
112
+
113
+    private function getAllMountedStorages(): array {
114
+        $query = $this->connection->getQueryBuilder();
115
+        $query->selectDistinct('storage_id')
116
+            ->from('mounts');
117
+        return $query->executeQuery()->fetchFirstColumn();
118
+    }
119
+
120
+    /**
121
+     * @param $argument
122
+     * @throws \Exception
123
+     */
124
+    protected function run($argument) {
125
+        if ($this->config->getSystemValueBool('files_no_background_scan', false)) {
126
+            return;
127
+        }
128
+
129
+        $usersScanned = 0;
130
+        $lastUser = '';
131
+        $user = $this->getUserToScan();
132
+        while ($user && $usersScanned < self::USERS_PER_SESSION && $lastUser !== $user) {
133
+            $this->runScanner($user);
134
+            $lastUser = $user;
135
+            $user = $this->getUserToScan();
136
+            $usersScanned += 1;
137
+        }
138
+
139
+        if ($lastUser === $user) {
140
+            $this->logger->warning("User $user still has unscanned files after running background scan, background scan might be stopped prematurely");
141
+        }
142
+    }
143 143
 }
Please login to merge, or discard this patch.
apps/files/lib/Command/RepairTree.php 1 patch
Indentation   +96 added lines, -96 removed lines patch added patch discarded remove patch
@@ -14,100 +14,100 @@
 block discarded – undo
14 14
 use Symfony\Component\Console\Output\OutputInterface;
15 15
 
16 16
 class RepairTree extends Command {
17
-	public const CHUNK_SIZE = 200;
18
-
19
-	public function __construct(
20
-		protected IDBConnection $connection,
21
-	) {
22
-		parent::__construct();
23
-	}
24
-
25
-	protected function configure(): void {
26
-		$this
27
-			->setName('files:repair-tree')
28
-			->setDescription('Try and repair malformed filesystem tree structures (may be necessary to run multiple times for nested malformations)')
29
-			->addOption('dry-run');
30
-	}
31
-
32
-	public function execute(InputInterface $input, OutputInterface $output): int {
33
-		$rows = $this->findBrokenTreeBits();
34
-		$fix = !$input->getOption('dry-run');
35
-
36
-		$output->writeln('Found ' . count($rows) . ' file entries with an invalid path');
37
-
38
-		if ($fix) {
39
-			$this->connection->beginTransaction();
40
-		}
41
-
42
-		$query = $this->connection->getQueryBuilder();
43
-		$query->update('filecache')
44
-			->set('path', $query->createParameter('path'))
45
-			->set('path_hash', $query->func()->md5($query->createParameter('path')))
46
-			->set('storage', $query->createParameter('storage'))
47
-			->where($query->expr()->eq('fileid', $query->createParameter('fileid')));
48
-
49
-		foreach ($rows as $row) {
50
-			$output->writeln("Path of file {$row['fileid']} is {$row['path']} but should be {$row['parent_path']}/{$row['name']} based on its parent", OutputInterface::VERBOSITY_VERBOSE);
51
-
52
-			if ($fix) {
53
-				$fileId = $this->getFileId((int)$row['parent_storage'], $row['parent_path'] . '/' . $row['name']);
54
-				if ($fileId > 0) {
55
-					$output->writeln("Cache entry has already be recreated with id $fileId, deleting instead");
56
-					$this->deleteById((int)$row['fileid']);
57
-				} else {
58
-					$query->setParameters([
59
-						'fileid' => $row['fileid'],
60
-						'path' => $row['parent_path'] . '/' . $row['name'],
61
-						'storage' => $row['parent_storage'],
62
-					]);
63
-					$query->executeStatement();
64
-				}
65
-			}
66
-		}
67
-
68
-		if ($fix) {
69
-			$this->connection->commit();
70
-		}
71
-
72
-		return self::SUCCESS;
73
-	}
74
-
75
-	private function getFileId(int $storage, string $path) {
76
-		$query = $this->connection->getQueryBuilder();
77
-		$query->select('fileid')
78
-			->from('filecache')
79
-			->where($query->expr()->eq('storage', $query->createNamedParameter($storage)))
80
-			->andWhere($query->expr()->eq('path_hash', $query->createNamedParameter(md5($path))));
81
-		return $query->executeQuery()->fetchOne();
82
-	}
83
-
84
-	private function deleteById(int $fileId): void {
85
-		$query = $this->connection->getQueryBuilder();
86
-		$query->delete('filecache')
87
-			->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId)));
88
-		$query->executeStatement();
89
-	}
90
-
91
-	private function findBrokenTreeBits(): array {
92
-		$query = $this->connection->getQueryBuilder();
93
-
94
-		$query->select('f.fileid', 'f.path', 'f.parent', 'f.name')
95
-			->selectAlias('p.path', 'parent_path')
96
-			->selectAlias('p.storage', 'parent_storage')
97
-			->from('filecache', 'f')
98
-			->innerJoin('f', 'filecache', 'p', $query->expr()->eq('f.parent', 'p.fileid'))
99
-			->where($query->expr()->orX(
100
-				$query->expr()->andX(
101
-					$query->expr()->neq('p.path_hash', $query->createNamedParameter(md5(''))),
102
-					$query->expr()->neq('f.path', $query->func()->concat('p.path', $query->func()->concat($query->createNamedParameter('/'), 'f.name')))
103
-				),
104
-				$query->expr()->andX(
105
-					$query->expr()->eq('p.path_hash', $query->createNamedParameter(md5(''))),
106
-					$query->expr()->neq('f.path', 'f.name')
107
-				),
108
-				$query->expr()->neq('f.storage', 'p.storage')
109
-			));
110
-
111
-		return $query->executeQuery()->fetchAllAssociative();
112
-	}
17
+    public const CHUNK_SIZE = 200;
18
+
19
+    public function __construct(
20
+        protected IDBConnection $connection,
21
+    ) {
22
+        parent::__construct();
23
+    }
24
+
25
+    protected function configure(): void {
26
+        $this
27
+            ->setName('files:repair-tree')
28
+            ->setDescription('Try and repair malformed filesystem tree structures (may be necessary to run multiple times for nested malformations)')
29
+            ->addOption('dry-run');
30
+    }
31
+
32
+    public function execute(InputInterface $input, OutputInterface $output): int {
33
+        $rows = $this->findBrokenTreeBits();
34
+        $fix = !$input->getOption('dry-run');
35
+
36
+        $output->writeln('Found ' . count($rows) . ' file entries with an invalid path');
37
+
38
+        if ($fix) {
39
+            $this->connection->beginTransaction();
40
+        }
41
+
42
+        $query = $this->connection->getQueryBuilder();
43
+        $query->update('filecache')
44
+            ->set('path', $query->createParameter('path'))
45
+            ->set('path_hash', $query->func()->md5($query->createParameter('path')))
46
+            ->set('storage', $query->createParameter('storage'))
47
+            ->where($query->expr()->eq('fileid', $query->createParameter('fileid')));
48
+
49
+        foreach ($rows as $row) {
50
+            $output->writeln("Path of file {$row['fileid']} is {$row['path']} but should be {$row['parent_path']}/{$row['name']} based on its parent", OutputInterface::VERBOSITY_VERBOSE);
51
+
52
+            if ($fix) {
53
+                $fileId = $this->getFileId((int)$row['parent_storage'], $row['parent_path'] . '/' . $row['name']);
54
+                if ($fileId > 0) {
55
+                    $output->writeln("Cache entry has already be recreated with id $fileId, deleting instead");
56
+                    $this->deleteById((int)$row['fileid']);
57
+                } else {
58
+                    $query->setParameters([
59
+                        'fileid' => $row['fileid'],
60
+                        'path' => $row['parent_path'] . '/' . $row['name'],
61
+                        'storage' => $row['parent_storage'],
62
+                    ]);
63
+                    $query->executeStatement();
64
+                }
65
+            }
66
+        }
67
+
68
+        if ($fix) {
69
+            $this->connection->commit();
70
+        }
71
+
72
+        return self::SUCCESS;
73
+    }
74
+
75
+    private function getFileId(int $storage, string $path) {
76
+        $query = $this->connection->getQueryBuilder();
77
+        $query->select('fileid')
78
+            ->from('filecache')
79
+            ->where($query->expr()->eq('storage', $query->createNamedParameter($storage)))
80
+            ->andWhere($query->expr()->eq('path_hash', $query->createNamedParameter(md5($path))));
81
+        return $query->executeQuery()->fetchOne();
82
+    }
83
+
84
+    private function deleteById(int $fileId): void {
85
+        $query = $this->connection->getQueryBuilder();
86
+        $query->delete('filecache')
87
+            ->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId)));
88
+        $query->executeStatement();
89
+    }
90
+
91
+    private function findBrokenTreeBits(): array {
92
+        $query = $this->connection->getQueryBuilder();
93
+
94
+        $query->select('f.fileid', 'f.path', 'f.parent', 'f.name')
95
+            ->selectAlias('p.path', 'parent_path')
96
+            ->selectAlias('p.storage', 'parent_storage')
97
+            ->from('filecache', 'f')
98
+            ->innerJoin('f', 'filecache', 'p', $query->expr()->eq('f.parent', 'p.fileid'))
99
+            ->where($query->expr()->orX(
100
+                $query->expr()->andX(
101
+                    $query->expr()->neq('p.path_hash', $query->createNamedParameter(md5(''))),
102
+                    $query->expr()->neq('f.path', $query->func()->concat('p.path', $query->func()->concat($query->createNamedParameter('/'), 'f.name')))
103
+                ),
104
+                $query->expr()->andX(
105
+                    $query->expr()->eq('p.path_hash', $query->createNamedParameter(md5(''))),
106
+                    $query->expr()->neq('f.path', 'f.name')
107
+                ),
108
+                $query->expr()->neq('f.storage', 'p.storage')
109
+            ));
110
+
111
+        return $query->executeQuery()->fetchAllAssociative();
112
+    }
113 113
 }
Please login to merge, or discard this patch.
apps/files/lib/Command/DeleteOrphanedFiles.php 2 patches
Indentation   +147 added lines, -147 removed lines patch added patch discarded remove patch
@@ -18,151 +18,151 @@
 block discarded – undo
18 18
  * Delete all file entries that have no matching entries in the storage table.
19 19
  */
20 20
 class DeleteOrphanedFiles extends Command {
21
-	public const CHUNK_SIZE = 200;
22
-
23
-	public function __construct(
24
-		protected IDBConnection $connection,
25
-	) {
26
-		parent::__construct();
27
-	}
28
-
29
-	protected function configure(): void {
30
-		$this
31
-			->setName('files:cleanup')
32
-			->setDescription('Clean up orphaned filecache and mount entries')
33
-			->setHelp('Deletes orphaned filecache and mount entries (those without an existing storage).')
34
-			->addOption('skip-filecache-extended', null, InputOption::VALUE_NONE, 'don\'t remove orphaned entries from filecache_extended');
35
-	}
36
-
37
-	public function execute(InputInterface $input, OutputInterface $output): int {
38
-		$fileIdsByStorage = [];
39
-
40
-		$deletedStorages = array_diff($this->getReferencedStorages(), $this->getExistingStorages());
41
-
42
-		$deleteExtended = !$input->getOption('skip-filecache-extended');
43
-		if ($deleteExtended) {
44
-			$fileIdsByStorage = $this->getFileIdsForStorages($deletedStorages);
45
-		}
46
-
47
-		$deletedEntries = $this->cleanupOrphanedFileCache($deletedStorages);
48
-		$output->writeln("$deletedEntries orphaned file cache entries deleted");
49
-
50
-		if ($deleteExtended) {
51
-			$deletedFileCacheExtended = $this->cleanupOrphanedFileCacheExtended($fileIdsByStorage);
52
-			$output->writeln("$deletedFileCacheExtended orphaned file cache extended entries deleted");
53
-		}
54
-
55
-		$deletedMounts = $this->cleanupOrphanedMounts();
56
-		$output->writeln("$deletedMounts orphaned mount entries deleted");
57
-
58
-		return self::SUCCESS;
59
-	}
60
-
61
-	private function getReferencedStorages(): array {
62
-		$query = $this->connection->getQueryBuilder();
63
-		$query->select('storage')
64
-			->from('filecache')
65
-			->groupBy('storage')
66
-			->runAcrossAllShards();
67
-		return $query->executeQuery()->fetchFirstColumn();
68
-	}
69
-
70
-	private function getExistingStorages(): array {
71
-		$query = $this->connection->getQueryBuilder();
72
-		$query->select('numeric_id')
73
-			->from('storages')
74
-			->groupBy('numeric_id');
75
-		return $query->executeQuery()->fetchFirstColumn();
76
-	}
77
-
78
-	/**
79
-	 * @param int[] $storageIds
80
-	 * @return array<int, int[]>
81
-	 */
82
-	private function getFileIdsForStorages(array $storageIds): array {
83
-		$query = $this->connection->getQueryBuilder();
84
-		$query->select('storage', 'fileid')
85
-			->from('filecache')
86
-			->where($query->expr()->in('storage', $query->createParameter('storage_ids')));
87
-
88
-		$result = [];
89
-		$storageIdChunks = array_chunk($storageIds, self::CHUNK_SIZE);
90
-		foreach ($storageIdChunks as $storageIdChunk) {
91
-			$query->setParameter('storage_ids', $storageIdChunk, IQueryBuilder::PARAM_INT_ARRAY);
92
-			$chunk = $query->executeQuery()->fetchAllAssociative();
93
-			foreach ($chunk as $row) {
94
-				$result[$row['storage']][] = $row['fileid'];
95
-			}
96
-		}
97
-		return $result;
98
-	}
99
-
100
-	private function cleanupOrphanedFileCache(array $deletedStorages): int {
101
-		$deletedEntries = 0;
102
-
103
-		$deleteQuery = $this->connection->getQueryBuilder();
104
-		$deleteQuery->delete('filecache')
105
-			->where($deleteQuery->expr()->in('storage', $deleteQuery->createParameter('storage_ids')));
106
-
107
-		$deletedStorageChunks = array_chunk($deletedStorages, self::CHUNK_SIZE);
108
-		foreach ($deletedStorageChunks as $deletedStorageChunk) {
109
-			$deleteQuery->setParameter('storage_ids', $deletedStorageChunk, IQueryBuilder::PARAM_INT_ARRAY);
110
-			$deletedEntries += $deleteQuery->executeStatement();
111
-		}
112
-
113
-		return $deletedEntries;
114
-	}
115
-
116
-	/**
117
-	 * @param array<int, int[]> $fileIdsByStorage
118
-	 * @return int
119
-	 */
120
-	private function cleanupOrphanedFileCacheExtended(array $fileIdsByStorage): int {
121
-		$deletedEntries = 0;
122
-
123
-		$deleteQuery = $this->connection->getQueryBuilder();
124
-		$deleteQuery->delete('filecache_extended')
125
-			->where($deleteQuery->expr()->in('fileid', $deleteQuery->createParameter('file_ids')));
126
-
127
-		foreach ($fileIdsByStorage as $storageId => $fileIds) {
128
-			$deleteQuery->hintShardKey('storage', $storageId, true);
129
-			$fileChunks = array_chunk($fileIds, self::CHUNK_SIZE);
130
-			foreach ($fileChunks as $fileChunk) {
131
-				$deleteQuery->setParameter('file_ids', $fileChunk, IQueryBuilder::PARAM_INT_ARRAY);
132
-				$deletedEntries += $deleteQuery->executeStatement();
133
-			}
134
-		}
135
-
136
-		return $deletedEntries;
137
-	}
138
-
139
-	private function cleanupOrphanedMounts(): int {
140
-		$deletedEntries = 0;
141
-
142
-		$query = $this->connection->getQueryBuilder();
143
-		$query->select('m.storage_id')
144
-			->from('mounts', 'm')
145
-			->where($query->expr()->isNull('s.numeric_id'))
146
-			->leftJoin('m', 'storages', 's', $query->expr()->eq('m.storage_id', 's.numeric_id'))
147
-			->groupBy('storage_id')
148
-			->setMaxResults(self::CHUNK_SIZE);
149
-
150
-		$deleteQuery = $this->connection->getQueryBuilder();
151
-		$deleteQuery->delete('mounts')
152
-			->where($deleteQuery->expr()->eq('storage_id', $deleteQuery->createParameter('storageid')));
153
-
154
-		$deletedInLastChunk = self::CHUNK_SIZE;
155
-		while ($deletedInLastChunk === self::CHUNK_SIZE) {
156
-			$deletedInLastChunk = 0;
157
-			$result = $query->executeQuery();
158
-			while ($row = $result->fetchAssociative()) {
159
-				$deletedInLastChunk++;
160
-				$deletedEntries += $deleteQuery->setParameter('storageid', (int)$row['storage_id'])
161
-					->executeStatement();
162
-			}
163
-			$result->closeCursor();
164
-		}
165
-
166
-		return $deletedEntries;
167
-	}
21
+    public const CHUNK_SIZE = 200;
22
+
23
+    public function __construct(
24
+        protected IDBConnection $connection,
25
+    ) {
26
+        parent::__construct();
27
+    }
28
+
29
+    protected function configure(): void {
30
+        $this
31
+            ->setName('files:cleanup')
32
+            ->setDescription('Clean up orphaned filecache and mount entries')
33
+            ->setHelp('Deletes orphaned filecache and mount entries (those without an existing storage).')
34
+            ->addOption('skip-filecache-extended', null, InputOption::VALUE_NONE, 'don\'t remove orphaned entries from filecache_extended');
35
+    }
36
+
37
+    public function execute(InputInterface $input, OutputInterface $output): int {
38
+        $fileIdsByStorage = [];
39
+
40
+        $deletedStorages = array_diff($this->getReferencedStorages(), $this->getExistingStorages());
41
+
42
+        $deleteExtended = !$input->getOption('skip-filecache-extended');
43
+        if ($deleteExtended) {
44
+            $fileIdsByStorage = $this->getFileIdsForStorages($deletedStorages);
45
+        }
46
+
47
+        $deletedEntries = $this->cleanupOrphanedFileCache($deletedStorages);
48
+        $output->writeln("$deletedEntries orphaned file cache entries deleted");
49
+
50
+        if ($deleteExtended) {
51
+            $deletedFileCacheExtended = $this->cleanupOrphanedFileCacheExtended($fileIdsByStorage);
52
+            $output->writeln("$deletedFileCacheExtended orphaned file cache extended entries deleted");
53
+        }
54
+
55
+        $deletedMounts = $this->cleanupOrphanedMounts();
56
+        $output->writeln("$deletedMounts orphaned mount entries deleted");
57
+
58
+        return self::SUCCESS;
59
+    }
60
+
61
+    private function getReferencedStorages(): array {
62
+        $query = $this->connection->getQueryBuilder();
63
+        $query->select('storage')
64
+            ->from('filecache')
65
+            ->groupBy('storage')
66
+            ->runAcrossAllShards();
67
+        return $query->executeQuery()->fetchFirstColumn();
68
+    }
69
+
70
+    private function getExistingStorages(): array {
71
+        $query = $this->connection->getQueryBuilder();
72
+        $query->select('numeric_id')
73
+            ->from('storages')
74
+            ->groupBy('numeric_id');
75
+        return $query->executeQuery()->fetchFirstColumn();
76
+    }
77
+
78
+    /**
79
+     * @param int[] $storageIds
80
+     * @return array<int, int[]>
81
+     */
82
+    private function getFileIdsForStorages(array $storageIds): array {
83
+        $query = $this->connection->getQueryBuilder();
84
+        $query->select('storage', 'fileid')
85
+            ->from('filecache')
86
+            ->where($query->expr()->in('storage', $query->createParameter('storage_ids')));
87
+
88
+        $result = [];
89
+        $storageIdChunks = array_chunk($storageIds, self::CHUNK_SIZE);
90
+        foreach ($storageIdChunks as $storageIdChunk) {
91
+            $query->setParameter('storage_ids', $storageIdChunk, IQueryBuilder::PARAM_INT_ARRAY);
92
+            $chunk = $query->executeQuery()->fetchAllAssociative();
93
+            foreach ($chunk as $row) {
94
+                $result[$row['storage']][] = $row['fileid'];
95
+            }
96
+        }
97
+        return $result;
98
+    }
99
+
100
+    private function cleanupOrphanedFileCache(array $deletedStorages): int {
101
+        $deletedEntries = 0;
102
+
103
+        $deleteQuery = $this->connection->getQueryBuilder();
104
+        $deleteQuery->delete('filecache')
105
+            ->where($deleteQuery->expr()->in('storage', $deleteQuery->createParameter('storage_ids')));
106
+
107
+        $deletedStorageChunks = array_chunk($deletedStorages, self::CHUNK_SIZE);
108
+        foreach ($deletedStorageChunks as $deletedStorageChunk) {
109
+            $deleteQuery->setParameter('storage_ids', $deletedStorageChunk, IQueryBuilder::PARAM_INT_ARRAY);
110
+            $deletedEntries += $deleteQuery->executeStatement();
111
+        }
112
+
113
+        return $deletedEntries;
114
+    }
115
+
116
+    /**
117
+     * @param array<int, int[]> $fileIdsByStorage
118
+     * @return int
119
+     */
120
+    private function cleanupOrphanedFileCacheExtended(array $fileIdsByStorage): int {
121
+        $deletedEntries = 0;
122
+
123
+        $deleteQuery = $this->connection->getQueryBuilder();
124
+        $deleteQuery->delete('filecache_extended')
125
+            ->where($deleteQuery->expr()->in('fileid', $deleteQuery->createParameter('file_ids')));
126
+
127
+        foreach ($fileIdsByStorage as $storageId => $fileIds) {
128
+            $deleteQuery->hintShardKey('storage', $storageId, true);
129
+            $fileChunks = array_chunk($fileIds, self::CHUNK_SIZE);
130
+            foreach ($fileChunks as $fileChunk) {
131
+                $deleteQuery->setParameter('file_ids', $fileChunk, IQueryBuilder::PARAM_INT_ARRAY);
132
+                $deletedEntries += $deleteQuery->executeStatement();
133
+            }
134
+        }
135
+
136
+        return $deletedEntries;
137
+    }
138
+
139
+    private function cleanupOrphanedMounts(): int {
140
+        $deletedEntries = 0;
141
+
142
+        $query = $this->connection->getQueryBuilder();
143
+        $query->select('m.storage_id')
144
+            ->from('mounts', 'm')
145
+            ->where($query->expr()->isNull('s.numeric_id'))
146
+            ->leftJoin('m', 'storages', 's', $query->expr()->eq('m.storage_id', 's.numeric_id'))
147
+            ->groupBy('storage_id')
148
+            ->setMaxResults(self::CHUNK_SIZE);
149
+
150
+        $deleteQuery = $this->connection->getQueryBuilder();
151
+        $deleteQuery->delete('mounts')
152
+            ->where($deleteQuery->expr()->eq('storage_id', $deleteQuery->createParameter('storageid')));
153
+
154
+        $deletedInLastChunk = self::CHUNK_SIZE;
155
+        while ($deletedInLastChunk === self::CHUNK_SIZE) {
156
+            $deletedInLastChunk = 0;
157
+            $result = $query->executeQuery();
158
+            while ($row = $result->fetchAssociative()) {
159
+                $deletedInLastChunk++;
160
+                $deletedEntries += $deleteQuery->setParameter('storageid', (int)$row['storage_id'])
161
+                    ->executeStatement();
162
+            }
163
+            $result->closeCursor();
164
+        }
165
+
166
+        return $deletedEntries;
167
+    }
168 168
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -157,7 +157,7 @@
 block discarded – undo
157 157
 			$result = $query->executeQuery();
158 158
 			while ($row = $result->fetchAssociative()) {
159 159
 				$deletedInLastChunk++;
160
-				$deletedEntries += $deleteQuery->setParameter('storageid', (int)$row['storage_id'])
160
+				$deletedEntries += $deleteQuery->setParameter('storageid', (int) $row['storage_id'])
161 161
 					->executeStatement();
162 162
 			}
163 163
 			$result->closeCursor();
Please login to merge, or discard this patch.
apps/workflowengine/lib/Manager.php 2 patches
Indentation   +663 added lines, -663 removed lines patch added patch discarded remove patch
@@ -46,667 +46,667 @@
 block discarded – undo
46 46
  * @psalm-type Check = array{id: int, class: class-string<ICheck>, operator: string, value: string, hash: string}
47 47
  */
48 48
 class Manager implements IManager {
49
-	/** @var array[] */
50
-	protected array $operations = [];
51
-
52
-	/** @var array<int, Check> */
53
-	protected array $checks = [];
54
-
55
-	/** @var IEntity[] */
56
-	protected array $registeredEntities = [];
57
-
58
-	/** @var IOperation[] */
59
-	protected array $registeredOperators = [];
60
-
61
-	/** @var ICheck[] */
62
-	protected array $registeredChecks = [];
63
-
64
-	/** @var CappedMemoryCache<int[]> */
65
-	protected CappedMemoryCache $operationsByScope;
66
-
67
-	public function __construct(
68
-		protected readonly IDBConnection $connection,
69
-		protected readonly ContainerInterface $container,
70
-		protected readonly IL10N $l,
71
-		protected readonly LoggerInterface $logger,
72
-		protected readonly IUserSession $session,
73
-		private readonly IEventDispatcher $dispatcher,
74
-		private readonly IAppConfig $appConfig,
75
-		private readonly ICacheFactory $cacheFactory,
76
-	) {
77
-		$this->operationsByScope = new CappedMemoryCache(64);
78
-	}
79
-
80
-	public function getRuleMatcher(): IRuleMatcher {
81
-		return new RuleMatcher(
82
-			$this->session,
83
-			$this->container,
84
-			$this->l,
85
-			$this,
86
-			$this->container->get(Logger::class)
87
-		);
88
-	}
89
-
90
-	public function getAllConfiguredEvents() {
91
-		$cache = $this->cacheFactory->createDistributed('flow');
92
-		$cached = $cache->get('events');
93
-		if ($cached !== null) {
94
-			return $cached;
95
-		}
96
-
97
-		$query = $this->connection->getQueryBuilder();
98
-
99
-		$query->select('class', 'entity')
100
-			->selectAlias($query->expr()->castColumn('events', IQueryBuilder::PARAM_STR), 'events')
101
-			->from('flow_operations')
102
-			->where($query->expr()->neq('events', $query->createNamedParameter('[]'), IQueryBuilder::PARAM_STR))
103
-			->groupBy('class', 'entity', $query->expr()->castColumn('events', IQueryBuilder::PARAM_STR));
104
-
105
-		$result = $query->executeQuery();
106
-		$operations = [];
107
-		while ($row = $result->fetchAssociative()) {
108
-			$eventNames = \json_decode($row['events']);
109
-
110
-			$operation = $row['class'];
111
-			$entity = $row['entity'];
112
-
113
-			$operations[$operation] = $operations[$row['class']] ?? [];
114
-			$operations[$operation][$entity] = $operations[$operation][$entity] ?? [];
115
-
116
-			$operations[$operation][$entity] = array_unique(array_merge($operations[$operation][$entity], $eventNames ?? []));
117
-		}
118
-		$result->closeCursor();
119
-
120
-		$cache->set('events', $operations, 3600);
121
-
122
-		return $operations;
123
-	}
124
-
125
-	/**
126
-	 * @param class-string<IOperation> $operationClass
127
-	 * @return ScopeContext[]
128
-	 */
129
-	public function getAllConfiguredScopesForOperation(string $operationClass): array {
130
-		/** @var array<class-string<IOperation>, ScopeContext[]> $scopesByOperation */
131
-		static $scopesByOperation = [];
132
-		if (isset($scopesByOperation[$operationClass])) {
133
-			return $scopesByOperation[$operationClass];
134
-		}
135
-
136
-		try {
137
-			/** @var IOperation $operation */
138
-			$operation = $this->container->get($operationClass);
139
-		} catch (ContainerExceptionInterface $e) {
140
-			return [];
141
-		}
142
-
143
-		$query = $this->connection->getQueryBuilder();
144
-
145
-		$query->selectDistinct('s.type')
146
-			->addSelect('s.value')
147
-			->from('flow_operations', 'o')
148
-			->leftJoin('o', 'flow_operations_scope', 's', $query->expr()->eq('o.id', 's.operation_id'))
149
-			->where($query->expr()->eq('o.class', $query->createParameter('operationClass')));
150
-
151
-		$query->setParameters(['operationClass' => $operationClass]);
152
-		$result = $query->executeQuery();
153
-
154
-		$scopesByOperation[$operationClass] = [];
155
-		while ($row = $result->fetchAssociative()) {
156
-			$scope = new ScopeContext($row['type'], $row['value']);
157
-
158
-			if (!$operation->isAvailableForScope((int)$row['type'])) {
159
-				continue;
160
-			}
161
-
162
-			$scopesByOperation[$operationClass][$scope->getHash()] = $scope;
163
-		}
164
-
165
-		return $scopesByOperation[$operationClass];
166
-	}
167
-
168
-	public function getAllOperations(ScopeContext $scopeContext): array {
169
-		if (isset($this->operations[$scopeContext->getHash()])) {
170
-			return $this->operations[$scopeContext->getHash()];
171
-		}
172
-
173
-		$query = $this->connection->getQueryBuilder();
174
-
175
-		$query->select('o.*')
176
-			->selectAlias('s.type', 'scope_type')
177
-			->selectAlias('s.value', 'scope_actor_id')
178
-			->from('flow_operations', 'o')
179
-			->leftJoin('o', 'flow_operations_scope', 's', $query->expr()->eq('o.id', 's.operation_id'))
180
-			->where($query->expr()->eq('s.type', $query->createParameter('scope')));
181
-
182
-		if ($scopeContext->getScope() === IManager::SCOPE_USER) {
183
-			$query->andWhere($query->expr()->eq('s.value', $query->createParameter('scopeId')));
184
-		}
185
-
186
-		$query->setParameters(['scope' => $scopeContext->getScope(), 'scopeId' => $scopeContext->getScopeId()]);
187
-		$result = $query->executeQuery();
188
-
189
-		$this->operations[$scopeContext->getHash()] = [];
190
-		while ($row = $result->fetchAssociative()) {
191
-			try {
192
-				/** @var IOperation $operation */
193
-				$operation = $this->container->get($row['class']);
194
-			} catch (ContainerExceptionInterface $e) {
195
-				continue;
196
-			}
197
-
198
-			if (!$operation->isAvailableForScope((int)$row['scope_type'])) {
199
-				continue;
200
-			}
201
-
202
-			if (!isset($this->operations[$scopeContext->getHash()][$row['class']])) {
203
-				$this->operations[$scopeContext->getHash()][$row['class']] = [];
204
-			}
205
-			$this->operations[$scopeContext->getHash()][$row['class']][] = $row;
206
-		}
207
-
208
-		return $this->operations[$scopeContext->getHash()];
209
-	}
210
-
211
-	public function getOperations(string $class, ScopeContext $scopeContext): array {
212
-		if (!isset($this->operations[$scopeContext->getHash()])) {
213
-			$this->getAllOperations($scopeContext);
214
-		}
215
-		return $this->operations[$scopeContext->getHash()][$class] ?? [];
216
-	}
217
-
218
-	/**
219
-	 * @param int $id
220
-	 * @return array
221
-	 * @throws \UnexpectedValueException
222
-	 */
223
-	protected function getOperation($id) {
224
-		$query = $this->connection->getQueryBuilder();
225
-		$query->select('*')
226
-			->from('flow_operations')
227
-			->where($query->expr()->eq('id', $query->createNamedParameter($id)));
228
-		$result = $query->executeQuery();
229
-		$row = $result->fetchAssociative();
230
-		$result->closeCursor();
231
-
232
-		if ($row) {
233
-			return $row;
234
-		}
235
-
236
-		throw new \UnexpectedValueException($this->l->t('Operation #%s does not exist', [$id]));
237
-	}
238
-
239
-	protected function insertOperation(
240
-		string $class,
241
-		string $name,
242
-		array $checkIds,
243
-		string $operation,
244
-		string $entity,
245
-		array $events,
246
-	): int {
247
-		$query = $this->connection->getQueryBuilder();
248
-		$query->insert('flow_operations')
249
-			->values([
250
-				'class' => $query->createNamedParameter($class),
251
-				'name' => $query->createNamedParameter($name),
252
-				'checks' => $query->createNamedParameter(json_encode(array_unique($checkIds))),
253
-				'operation' => $query->createNamedParameter($operation),
254
-				'entity' => $query->createNamedParameter($entity),
255
-				'events' => $query->createNamedParameter(json_encode($events))
256
-			]);
257
-		$query->executeStatement();
258
-
259
-		$this->cacheFactory->createDistributed('flow')->remove('events');
260
-
261
-		return $query->getLastInsertId();
262
-	}
263
-
264
-	/**
265
-	 * @param string $class
266
-	 * @param string $name
267
-	 * @param array<int, Check> $checks
268
-	 * @param string $operation
269
-	 * @return array The added operation
270
-	 * @throws \UnexpectedValueException
271
-	 * @throws Exception
272
-	 */
273
-	public function addOperation(
274
-		string $class,
275
-		string $name,
276
-		array $checks,
277
-		string $operation,
278
-		ScopeContext $scope,
279
-		string $entity,
280
-		array $events,
281
-	) {
282
-		$this->validateOperation($class, $name, $checks, $operation, $scope, $entity, $events);
283
-
284
-		$this->connection->beginTransaction();
285
-
286
-		try {
287
-			$checkIds = [];
288
-			foreach ($checks as $check) {
289
-				$checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
290
-			}
291
-
292
-			$id = $this->insertOperation($class, $name, $checkIds, $operation, $entity, $events);
293
-			$this->addScope($id, $scope);
294
-
295
-			$this->connection->commit();
296
-		} catch (Exception $e) {
297
-			$this->connection->rollBack();
298
-			throw $e;
299
-		}
300
-
301
-		return $this->getOperation($id);
302
-	}
303
-
304
-	protected function canModify(int $id, ScopeContext $scopeContext):bool {
305
-		if (isset($this->operationsByScope[$scopeContext->getHash()])) {
306
-			return in_array($id, $this->operationsByScope[$scopeContext->getHash()], true);
307
-		}
308
-
309
-		$qb = $this->connection->getQueryBuilder();
310
-		$qb = $qb->select('o.id')
311
-			->from('flow_operations', 'o')
312
-			->leftJoin('o', 'flow_operations_scope', 's', $qb->expr()->eq('o.id', 's.operation_id'))
313
-			->where($qb->expr()->eq('s.type', $qb->createParameter('scope')));
314
-
315
-		if ($scopeContext->getScope() !== IManager::SCOPE_ADMIN) {
316
-			$qb->andWhere($qb->expr()->eq('s.value', $qb->createParameter('scopeId')));
317
-		}
318
-
319
-		$qb->setParameters(['scope' => $scopeContext->getScope(), 'scopeId' => $scopeContext->getScopeId()]);
320
-		$result = $qb->executeQuery();
321
-
322
-		$operations = [];
323
-		while (($opId = $result->fetchOne()) !== false) {
324
-			$operations[] = (int)$opId;
325
-		}
326
-		$this->operationsByScope[$scopeContext->getHash()] = $operations;
327
-		$result->closeCursor();
328
-
329
-		return in_array($id, $this->operationsByScope[$scopeContext->getHash()], true);
330
-	}
331
-
332
-	/**
333
-	 * @param int $id
334
-	 * @param string $name
335
-	 * @param array[] $checks
336
-	 * @param string $operation
337
-	 * @return array The updated operation
338
-	 * @throws \UnexpectedValueException
339
-	 * @throws \DomainException
340
-	 * @throws Exception
341
-	 */
342
-	public function updateOperation(
343
-		int $id,
344
-		string $name,
345
-		array $checks,
346
-		string $operation,
347
-		ScopeContext $scopeContext,
348
-		string $entity,
349
-		array $events,
350
-	): array {
351
-		if (!$this->canModify($id, $scopeContext)) {
352
-			throw new \DomainException('Target operation not within scope');
353
-		};
354
-		$row = $this->getOperation($id);
355
-		$this->validateOperation($row['class'], $name, $checks, $operation, $scopeContext, $entity, $events);
356
-
357
-		$checkIds = [];
358
-		try {
359
-			$this->connection->beginTransaction();
360
-			foreach ($checks as $check) {
361
-				$checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
362
-			}
363
-
364
-			$query = $this->connection->getQueryBuilder();
365
-			$query->update('flow_operations')
366
-				->set('name', $query->createNamedParameter($name))
367
-				->set('checks', $query->createNamedParameter(json_encode(array_unique($checkIds))))
368
-				->set('operation', $query->createNamedParameter($operation))
369
-				->set('entity', $query->createNamedParameter($entity))
370
-				->set('events', $query->createNamedParameter(json_encode($events)))
371
-				->where($query->expr()->eq('id', $query->createNamedParameter($id)));
372
-			$query->executeStatement();
373
-			$this->connection->commit();
374
-		} catch (Exception $e) {
375
-			$this->connection->rollBack();
376
-			throw $e;
377
-		}
378
-		unset($this->operations[$scopeContext->getHash()]);
379
-		$this->cacheFactory->createDistributed('flow')->remove('events');
380
-
381
-		return $this->getOperation($id);
382
-	}
383
-
384
-	/**
385
-	 * @throws \UnexpectedValueException
386
-	 * @throws Exception
387
-	 * @throws \DomainException
388
-	 */
389
-	public function deleteOperation(int $id, ScopeContext $scopeContext): bool {
390
-		if (!$this->canModify($id, $scopeContext)) {
391
-			throw new \DomainException('Target operation not within scope');
392
-		};
393
-		$query = $this->connection->getQueryBuilder();
394
-		try {
395
-			$this->connection->beginTransaction();
396
-			$result = (bool)$query->delete('flow_operations')
397
-				->where($query->expr()->eq('id', $query->createNamedParameter($id)))
398
-				->executeStatement();
399
-			if ($result) {
400
-				$qb = $this->connection->getQueryBuilder();
401
-				$result = (bool)$qb->delete('flow_operations_scope')
402
-					->where($qb->expr()->eq('operation_id', $qb->createNamedParameter($id)))
403
-					->executeStatement();
404
-			}
405
-			$this->connection->commit();
406
-		} catch (Exception $e) {
407
-			$this->connection->rollBack();
408
-			throw $e;
409
-		}
410
-
411
-		if (isset($this->operations[$scopeContext->getHash()])) {
412
-			unset($this->operations[$scopeContext->getHash()]);
413
-		}
414
-
415
-		$this->cacheFactory->createDistributed('flow')->remove('events');
416
-
417
-		return $result;
418
-	}
419
-
420
-	/**
421
-	 * @param class-string<IEntity> $entity
422
-	 * @param array $events
423
-	 */
424
-	protected function validateEvents(string $entity, array $events, IOperation $operation): void {
425
-		try {
426
-			$instance = $this->container->get($entity);
427
-		} catch (ContainerExceptionInterface $e) {
428
-			throw new \UnexpectedValueException($this->l->t('Entity %s does not exist', [$entity]));
429
-		}
430
-
431
-		if (!$instance instanceof IEntity) {
432
-			throw new \UnexpectedValueException($this->l->t('Entity %s is invalid', [$entity]));
433
-		}
434
-
435
-		if (empty($events)) {
436
-			if (!$operation instanceof IComplexOperation) {
437
-				throw new \UnexpectedValueException($this->l->t('No events are chosen.'));
438
-			}
439
-			return;
440
-		}
441
-
442
-		$availableEvents = [];
443
-		foreach ($instance->getEvents() as $event) {
444
-			/** @var IEntityEvent $event */
445
-			$availableEvents[] = $event->getEventName();
446
-		}
447
-
448
-		$diff = array_diff($events, $availableEvents);
449
-		if (!empty($diff)) {
450
-			throw new \UnexpectedValueException($this->l->t('Entity %s has no event %s', [$entity, array_shift($diff)]));
451
-		}
452
-	}
453
-
454
-	/**
455
-	 * @param class-string<IOperation> $class
456
-	 * @param array<int, Check> $checks
457
-	 * @param array $events
458
-	 * @throws \UnexpectedValueException
459
-	 */
460
-	public function validateOperation(string $class, string $name, array $checks, string $operation, ScopeContext $scope, string $entity, array $events): void {
461
-		try {
462
-			/** @var IOperation $instance */
463
-			$instance = $this->container->get($class);
464
-		} catch (ContainerExceptionInterface $e) {
465
-			throw new \UnexpectedValueException($this->l->t('Operation %s does not exist', [$class]));
466
-		}
467
-
468
-		if (!($instance instanceof IOperation)) {
469
-			throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
470
-		}
471
-
472
-		if (!$instance->isAvailableForScope($scope->getScope())) {
473
-			throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
474
-		}
475
-
476
-		$this->validateEvents($entity, $events, $instance);
477
-
478
-		if (count($checks) === 0) {
479
-			throw new \UnexpectedValueException($this->l->t('At least one check needs to be provided'));
480
-		}
481
-
482
-		if (strlen($operation) > IManager::MAX_OPERATION_VALUE_BYTES) {
483
-			throw new \UnexpectedValueException($this->l->t('The provided operation data is too long'));
484
-		}
485
-
486
-		$instance->validateOperation($name, $checks, $operation);
487
-
488
-		foreach ($checks as $check) {
489
-			if (!is_string($check['class'])) {
490
-				throw new \UnexpectedValueException($this->l->t('Invalid check provided'));
491
-			}
492
-
493
-			try {
494
-				/** @var ICheck $instance */
495
-				$instance = $this->container->get($check['class']);
496
-			} catch (ContainerExceptionInterface) {
497
-				throw new \UnexpectedValueException($this->l->t('Check %s does not exist', [$class]));
498
-			}
499
-
500
-			if (!($instance instanceof ICheck)) {
501
-				throw new \UnexpectedValueException($this->l->t('Check %s is invalid', [$class]));
502
-			}
503
-
504
-			if (!empty($instance->supportedEntities())
505
-				&& !in_array($entity, $instance->supportedEntities())
506
-			) {
507
-				throw new \UnexpectedValueException($this->l->t('Check %s is not allowed with this entity', [$class]));
508
-			}
509
-
510
-			if (strlen((string)$check['value']) > IManager::MAX_CHECK_VALUE_BYTES) {
511
-				throw new \UnexpectedValueException($this->l->t('The provided check value is too long'));
512
-			}
513
-
514
-			$instance->validateCheck($check['operator'], $check['value']);
515
-		}
516
-	}
517
-
518
-	/**
519
-	 * @param int[] $checkIds
520
-	 * @return array<int, Check>
521
-	 */
522
-	public function getChecks(array $checkIds): array {
523
-		$checkIds = array_map('intval', $checkIds);
524
-
525
-		$checks = [];
526
-		foreach ($checkIds as $i => $checkId) {
527
-			if (isset($this->checks[$checkId])) {
528
-				$checks[$checkId] = $this->checks[$checkId];
529
-				unset($checkIds[$i]);
530
-			}
531
-		}
532
-
533
-		if (empty($checkIds)) {
534
-			return $checks;
535
-		}
536
-
537
-		$query = $this->connection->getQueryBuilder();
538
-		$query->select('*')
539
-			->from('flow_checks')
540
-			->where($query->expr()->in('id', $query->createNamedParameter($checkIds, IQueryBuilder::PARAM_INT_ARRAY)));
541
-		$result = $query->executeQuery();
542
-
543
-		while ($row = $result->fetchAssociative()) {
544
-			/** @var Check $row */
545
-			$this->checks[(int)$row['id']] = $row;
546
-			$checks[(int)$row['id']] = $row;
547
-		}
548
-		$result->closeCursor();
549
-
550
-		$checkIds = array_diff($checkIds, array_keys($checks));
551
-
552
-		if (!empty($checkIds)) {
553
-			$missingCheck = array_pop($checkIds);
554
-			throw new \UnexpectedValueException($this->l->t('Check #%s does not exist', (string)$missingCheck));
555
-		}
556
-
557
-		return $checks;
558
-	}
559
-
560
-	/**
561
-	 * @return int Check unique ID
562
-	 */
563
-	protected function addCheck(string $class, string $operator, string $value): int {
564
-		$hash = md5($class . '::' . $operator . '::' . $value);
565
-
566
-		$query = $this->connection->getQueryBuilder();
567
-		$query->select('id')
568
-			->from('flow_checks')
569
-			->where($query->expr()->eq('hash', $query->createNamedParameter($hash)));
570
-		$result = $query->executeQuery();
571
-
572
-		if ($row = $result->fetchAssociative()) {
573
-			$result->closeCursor();
574
-			return (int)$row['id'];
575
-		}
576
-
577
-		$query = $this->connection->getQueryBuilder();
578
-		$query->insert('flow_checks')
579
-			->values([
580
-				'class' => $query->createNamedParameter($class),
581
-				'operator' => $query->createNamedParameter($operator),
582
-				'value' => $query->createNamedParameter($value),
583
-				'hash' => $query->createNamedParameter($hash),
584
-			]);
585
-		$query->executeStatement();
586
-
587
-		return $query->getLastInsertId();
588
-	}
589
-
590
-	protected function addScope(int $operationId, ScopeContext $scope): void {
591
-		$query = $this->connection->getQueryBuilder();
592
-
593
-		$insertQuery = $query->insert('flow_operations_scope');
594
-		$insertQuery->values([
595
-			'operation_id' => $query->createNamedParameter($operationId),
596
-			'type' => $query->createNamedParameter($scope->getScope()),
597
-			'value' => $query->createNamedParameter($scope->getScopeId()),
598
-		]);
599
-		$insertQuery->executeStatement();
600
-	}
601
-
602
-	public function formatOperation(array $operation): array {
603
-		$checkIds = json_decode($operation['checks'], true);
604
-		$checks = $this->getChecks($checkIds);
605
-
606
-		$operation['checks'] = [];
607
-		foreach ($checks as $check) {
608
-			// Remove internal values
609
-			unset($check['id']);
610
-			unset($check['hash']);
611
-
612
-			$operation['checks'][] = $check;
613
-		}
614
-		$operation['events'] = json_decode($operation['events'], true) ?? [];
615
-
616
-
617
-		return $operation;
618
-	}
619
-
620
-	/**
621
-	 * @return IEntity[]
622
-	 */
623
-	public function getEntitiesList(): array {
624
-		$this->dispatcher->dispatchTyped(new RegisterEntitiesEvent($this));
625
-
626
-		return array_values(array_merge($this->getBuildInEntities(), $this->registeredEntities));
627
-	}
628
-
629
-	/**
630
-	 * @return IOperation[]
631
-	 */
632
-	public function getOperatorList(): array {
633
-		$this->dispatcher->dispatchTyped(new RegisterOperationsEvent($this));
634
-
635
-		return array_merge($this->getBuildInOperators(), $this->registeredOperators);
636
-	}
637
-
638
-	/**
639
-	 * @return ICheck[]
640
-	 */
641
-	public function getCheckList(): array {
642
-		$this->dispatcher->dispatchTyped(new RegisterChecksEvent($this));
643
-
644
-		return array_merge($this->getBuildInChecks(), $this->registeredChecks);
645
-	}
646
-
647
-	public function registerEntity(IEntity $entity): void {
648
-		$this->registeredEntities[get_class($entity)] = $entity;
649
-	}
650
-
651
-	public function registerOperation(IOperation $operator): void {
652
-		$this->registeredOperators[get_class($operator)] = $operator;
653
-	}
654
-
655
-	public function registerCheck(ICheck $check): void {
656
-		$this->registeredChecks[get_class($check)] = $check;
657
-	}
658
-
659
-	/**
660
-	 * @return IEntity[]
661
-	 */
662
-	protected function getBuildInEntities(): array {
663
-		try {
664
-			return [
665
-				File::class => $this->container->get(File::class),
666
-			];
667
-		} catch (ContainerExceptionInterface $e) {
668
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
669
-			return [];
670
-		}
671
-	}
672
-
673
-	/**
674
-	 * @return IOperation[]
675
-	 */
676
-	protected function getBuildInOperators(): array {
677
-		try {
678
-			return [
679
-				// None yet
680
-			];
681
-		} catch (ContainerExceptionInterface $e) {
682
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
683
-			return [];
684
-		}
685
-	}
686
-
687
-	/**
688
-	 * @return ICheck[]
689
-	 */
690
-	protected function getBuildInChecks(): array {
691
-		try {
692
-			return [
693
-				$this->container->get(FileMimeType::class),
694
-				$this->container->get(FileName::class),
695
-				$this->container->get(FileSize::class),
696
-				$this->container->get(FileSystemTags::class),
697
-				$this->container->get(RequestRemoteAddress::class),
698
-				$this->container->get(RequestTime::class),
699
-				$this->container->get(RequestURL::class),
700
-				$this->container->get(RequestUserAgent::class),
701
-				$this->container->get(UserGroupMembership::class),
702
-			];
703
-		} catch (ContainerExceptionInterface $e) {
704
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
705
-			return [];
706
-		}
707
-	}
708
-
709
-	public function isUserScopeEnabled(): bool {
710
-		return !$this->appConfig->getAppValueBool('user_scope_disabled');
711
-	}
49
+    /** @var array[] */
50
+    protected array $operations = [];
51
+
52
+    /** @var array<int, Check> */
53
+    protected array $checks = [];
54
+
55
+    /** @var IEntity[] */
56
+    protected array $registeredEntities = [];
57
+
58
+    /** @var IOperation[] */
59
+    protected array $registeredOperators = [];
60
+
61
+    /** @var ICheck[] */
62
+    protected array $registeredChecks = [];
63
+
64
+    /** @var CappedMemoryCache<int[]> */
65
+    protected CappedMemoryCache $operationsByScope;
66
+
67
+    public function __construct(
68
+        protected readonly IDBConnection $connection,
69
+        protected readonly ContainerInterface $container,
70
+        protected readonly IL10N $l,
71
+        protected readonly LoggerInterface $logger,
72
+        protected readonly IUserSession $session,
73
+        private readonly IEventDispatcher $dispatcher,
74
+        private readonly IAppConfig $appConfig,
75
+        private readonly ICacheFactory $cacheFactory,
76
+    ) {
77
+        $this->operationsByScope = new CappedMemoryCache(64);
78
+    }
79
+
80
+    public function getRuleMatcher(): IRuleMatcher {
81
+        return new RuleMatcher(
82
+            $this->session,
83
+            $this->container,
84
+            $this->l,
85
+            $this,
86
+            $this->container->get(Logger::class)
87
+        );
88
+    }
89
+
90
+    public function getAllConfiguredEvents() {
91
+        $cache = $this->cacheFactory->createDistributed('flow');
92
+        $cached = $cache->get('events');
93
+        if ($cached !== null) {
94
+            return $cached;
95
+        }
96
+
97
+        $query = $this->connection->getQueryBuilder();
98
+
99
+        $query->select('class', 'entity')
100
+            ->selectAlias($query->expr()->castColumn('events', IQueryBuilder::PARAM_STR), 'events')
101
+            ->from('flow_operations')
102
+            ->where($query->expr()->neq('events', $query->createNamedParameter('[]'), IQueryBuilder::PARAM_STR))
103
+            ->groupBy('class', 'entity', $query->expr()->castColumn('events', IQueryBuilder::PARAM_STR));
104
+
105
+        $result = $query->executeQuery();
106
+        $operations = [];
107
+        while ($row = $result->fetchAssociative()) {
108
+            $eventNames = \json_decode($row['events']);
109
+
110
+            $operation = $row['class'];
111
+            $entity = $row['entity'];
112
+
113
+            $operations[$operation] = $operations[$row['class']] ?? [];
114
+            $operations[$operation][$entity] = $operations[$operation][$entity] ?? [];
115
+
116
+            $operations[$operation][$entity] = array_unique(array_merge($operations[$operation][$entity], $eventNames ?? []));
117
+        }
118
+        $result->closeCursor();
119
+
120
+        $cache->set('events', $operations, 3600);
121
+
122
+        return $operations;
123
+    }
124
+
125
+    /**
126
+     * @param class-string<IOperation> $operationClass
127
+     * @return ScopeContext[]
128
+     */
129
+    public function getAllConfiguredScopesForOperation(string $operationClass): array {
130
+        /** @var array<class-string<IOperation>, ScopeContext[]> $scopesByOperation */
131
+        static $scopesByOperation = [];
132
+        if (isset($scopesByOperation[$operationClass])) {
133
+            return $scopesByOperation[$operationClass];
134
+        }
135
+
136
+        try {
137
+            /** @var IOperation $operation */
138
+            $operation = $this->container->get($operationClass);
139
+        } catch (ContainerExceptionInterface $e) {
140
+            return [];
141
+        }
142
+
143
+        $query = $this->connection->getQueryBuilder();
144
+
145
+        $query->selectDistinct('s.type')
146
+            ->addSelect('s.value')
147
+            ->from('flow_operations', 'o')
148
+            ->leftJoin('o', 'flow_operations_scope', 's', $query->expr()->eq('o.id', 's.operation_id'))
149
+            ->where($query->expr()->eq('o.class', $query->createParameter('operationClass')));
150
+
151
+        $query->setParameters(['operationClass' => $operationClass]);
152
+        $result = $query->executeQuery();
153
+
154
+        $scopesByOperation[$operationClass] = [];
155
+        while ($row = $result->fetchAssociative()) {
156
+            $scope = new ScopeContext($row['type'], $row['value']);
157
+
158
+            if (!$operation->isAvailableForScope((int)$row['type'])) {
159
+                continue;
160
+            }
161
+
162
+            $scopesByOperation[$operationClass][$scope->getHash()] = $scope;
163
+        }
164
+
165
+        return $scopesByOperation[$operationClass];
166
+    }
167
+
168
+    public function getAllOperations(ScopeContext $scopeContext): array {
169
+        if (isset($this->operations[$scopeContext->getHash()])) {
170
+            return $this->operations[$scopeContext->getHash()];
171
+        }
172
+
173
+        $query = $this->connection->getQueryBuilder();
174
+
175
+        $query->select('o.*')
176
+            ->selectAlias('s.type', 'scope_type')
177
+            ->selectAlias('s.value', 'scope_actor_id')
178
+            ->from('flow_operations', 'o')
179
+            ->leftJoin('o', 'flow_operations_scope', 's', $query->expr()->eq('o.id', 's.operation_id'))
180
+            ->where($query->expr()->eq('s.type', $query->createParameter('scope')));
181
+
182
+        if ($scopeContext->getScope() === IManager::SCOPE_USER) {
183
+            $query->andWhere($query->expr()->eq('s.value', $query->createParameter('scopeId')));
184
+        }
185
+
186
+        $query->setParameters(['scope' => $scopeContext->getScope(), 'scopeId' => $scopeContext->getScopeId()]);
187
+        $result = $query->executeQuery();
188
+
189
+        $this->operations[$scopeContext->getHash()] = [];
190
+        while ($row = $result->fetchAssociative()) {
191
+            try {
192
+                /** @var IOperation $operation */
193
+                $operation = $this->container->get($row['class']);
194
+            } catch (ContainerExceptionInterface $e) {
195
+                continue;
196
+            }
197
+
198
+            if (!$operation->isAvailableForScope((int)$row['scope_type'])) {
199
+                continue;
200
+            }
201
+
202
+            if (!isset($this->operations[$scopeContext->getHash()][$row['class']])) {
203
+                $this->operations[$scopeContext->getHash()][$row['class']] = [];
204
+            }
205
+            $this->operations[$scopeContext->getHash()][$row['class']][] = $row;
206
+        }
207
+
208
+        return $this->operations[$scopeContext->getHash()];
209
+    }
210
+
211
+    public function getOperations(string $class, ScopeContext $scopeContext): array {
212
+        if (!isset($this->operations[$scopeContext->getHash()])) {
213
+            $this->getAllOperations($scopeContext);
214
+        }
215
+        return $this->operations[$scopeContext->getHash()][$class] ?? [];
216
+    }
217
+
218
+    /**
219
+     * @param int $id
220
+     * @return array
221
+     * @throws \UnexpectedValueException
222
+     */
223
+    protected function getOperation($id) {
224
+        $query = $this->connection->getQueryBuilder();
225
+        $query->select('*')
226
+            ->from('flow_operations')
227
+            ->where($query->expr()->eq('id', $query->createNamedParameter($id)));
228
+        $result = $query->executeQuery();
229
+        $row = $result->fetchAssociative();
230
+        $result->closeCursor();
231
+
232
+        if ($row) {
233
+            return $row;
234
+        }
235
+
236
+        throw new \UnexpectedValueException($this->l->t('Operation #%s does not exist', [$id]));
237
+    }
238
+
239
+    protected function insertOperation(
240
+        string $class,
241
+        string $name,
242
+        array $checkIds,
243
+        string $operation,
244
+        string $entity,
245
+        array $events,
246
+    ): int {
247
+        $query = $this->connection->getQueryBuilder();
248
+        $query->insert('flow_operations')
249
+            ->values([
250
+                'class' => $query->createNamedParameter($class),
251
+                'name' => $query->createNamedParameter($name),
252
+                'checks' => $query->createNamedParameter(json_encode(array_unique($checkIds))),
253
+                'operation' => $query->createNamedParameter($operation),
254
+                'entity' => $query->createNamedParameter($entity),
255
+                'events' => $query->createNamedParameter(json_encode($events))
256
+            ]);
257
+        $query->executeStatement();
258
+
259
+        $this->cacheFactory->createDistributed('flow')->remove('events');
260
+
261
+        return $query->getLastInsertId();
262
+    }
263
+
264
+    /**
265
+     * @param string $class
266
+     * @param string $name
267
+     * @param array<int, Check> $checks
268
+     * @param string $operation
269
+     * @return array The added operation
270
+     * @throws \UnexpectedValueException
271
+     * @throws Exception
272
+     */
273
+    public function addOperation(
274
+        string $class,
275
+        string $name,
276
+        array $checks,
277
+        string $operation,
278
+        ScopeContext $scope,
279
+        string $entity,
280
+        array $events,
281
+    ) {
282
+        $this->validateOperation($class, $name, $checks, $operation, $scope, $entity, $events);
283
+
284
+        $this->connection->beginTransaction();
285
+
286
+        try {
287
+            $checkIds = [];
288
+            foreach ($checks as $check) {
289
+                $checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
290
+            }
291
+
292
+            $id = $this->insertOperation($class, $name, $checkIds, $operation, $entity, $events);
293
+            $this->addScope($id, $scope);
294
+
295
+            $this->connection->commit();
296
+        } catch (Exception $e) {
297
+            $this->connection->rollBack();
298
+            throw $e;
299
+        }
300
+
301
+        return $this->getOperation($id);
302
+    }
303
+
304
+    protected function canModify(int $id, ScopeContext $scopeContext):bool {
305
+        if (isset($this->operationsByScope[$scopeContext->getHash()])) {
306
+            return in_array($id, $this->operationsByScope[$scopeContext->getHash()], true);
307
+        }
308
+
309
+        $qb = $this->connection->getQueryBuilder();
310
+        $qb = $qb->select('o.id')
311
+            ->from('flow_operations', 'o')
312
+            ->leftJoin('o', 'flow_operations_scope', 's', $qb->expr()->eq('o.id', 's.operation_id'))
313
+            ->where($qb->expr()->eq('s.type', $qb->createParameter('scope')));
314
+
315
+        if ($scopeContext->getScope() !== IManager::SCOPE_ADMIN) {
316
+            $qb->andWhere($qb->expr()->eq('s.value', $qb->createParameter('scopeId')));
317
+        }
318
+
319
+        $qb->setParameters(['scope' => $scopeContext->getScope(), 'scopeId' => $scopeContext->getScopeId()]);
320
+        $result = $qb->executeQuery();
321
+
322
+        $operations = [];
323
+        while (($opId = $result->fetchOne()) !== false) {
324
+            $operations[] = (int)$opId;
325
+        }
326
+        $this->operationsByScope[$scopeContext->getHash()] = $operations;
327
+        $result->closeCursor();
328
+
329
+        return in_array($id, $this->operationsByScope[$scopeContext->getHash()], true);
330
+    }
331
+
332
+    /**
333
+     * @param int $id
334
+     * @param string $name
335
+     * @param array[] $checks
336
+     * @param string $operation
337
+     * @return array The updated operation
338
+     * @throws \UnexpectedValueException
339
+     * @throws \DomainException
340
+     * @throws Exception
341
+     */
342
+    public function updateOperation(
343
+        int $id,
344
+        string $name,
345
+        array $checks,
346
+        string $operation,
347
+        ScopeContext $scopeContext,
348
+        string $entity,
349
+        array $events,
350
+    ): array {
351
+        if (!$this->canModify($id, $scopeContext)) {
352
+            throw new \DomainException('Target operation not within scope');
353
+        };
354
+        $row = $this->getOperation($id);
355
+        $this->validateOperation($row['class'], $name, $checks, $operation, $scopeContext, $entity, $events);
356
+
357
+        $checkIds = [];
358
+        try {
359
+            $this->connection->beginTransaction();
360
+            foreach ($checks as $check) {
361
+                $checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
362
+            }
363
+
364
+            $query = $this->connection->getQueryBuilder();
365
+            $query->update('flow_operations')
366
+                ->set('name', $query->createNamedParameter($name))
367
+                ->set('checks', $query->createNamedParameter(json_encode(array_unique($checkIds))))
368
+                ->set('operation', $query->createNamedParameter($operation))
369
+                ->set('entity', $query->createNamedParameter($entity))
370
+                ->set('events', $query->createNamedParameter(json_encode($events)))
371
+                ->where($query->expr()->eq('id', $query->createNamedParameter($id)));
372
+            $query->executeStatement();
373
+            $this->connection->commit();
374
+        } catch (Exception $e) {
375
+            $this->connection->rollBack();
376
+            throw $e;
377
+        }
378
+        unset($this->operations[$scopeContext->getHash()]);
379
+        $this->cacheFactory->createDistributed('flow')->remove('events');
380
+
381
+        return $this->getOperation($id);
382
+    }
383
+
384
+    /**
385
+     * @throws \UnexpectedValueException
386
+     * @throws Exception
387
+     * @throws \DomainException
388
+     */
389
+    public function deleteOperation(int $id, ScopeContext $scopeContext): bool {
390
+        if (!$this->canModify($id, $scopeContext)) {
391
+            throw new \DomainException('Target operation not within scope');
392
+        };
393
+        $query = $this->connection->getQueryBuilder();
394
+        try {
395
+            $this->connection->beginTransaction();
396
+            $result = (bool)$query->delete('flow_operations')
397
+                ->where($query->expr()->eq('id', $query->createNamedParameter($id)))
398
+                ->executeStatement();
399
+            if ($result) {
400
+                $qb = $this->connection->getQueryBuilder();
401
+                $result = (bool)$qb->delete('flow_operations_scope')
402
+                    ->where($qb->expr()->eq('operation_id', $qb->createNamedParameter($id)))
403
+                    ->executeStatement();
404
+            }
405
+            $this->connection->commit();
406
+        } catch (Exception $e) {
407
+            $this->connection->rollBack();
408
+            throw $e;
409
+        }
410
+
411
+        if (isset($this->operations[$scopeContext->getHash()])) {
412
+            unset($this->operations[$scopeContext->getHash()]);
413
+        }
414
+
415
+        $this->cacheFactory->createDistributed('flow')->remove('events');
416
+
417
+        return $result;
418
+    }
419
+
420
+    /**
421
+     * @param class-string<IEntity> $entity
422
+     * @param array $events
423
+     */
424
+    protected function validateEvents(string $entity, array $events, IOperation $operation): void {
425
+        try {
426
+            $instance = $this->container->get($entity);
427
+        } catch (ContainerExceptionInterface $e) {
428
+            throw new \UnexpectedValueException($this->l->t('Entity %s does not exist', [$entity]));
429
+        }
430
+
431
+        if (!$instance instanceof IEntity) {
432
+            throw new \UnexpectedValueException($this->l->t('Entity %s is invalid', [$entity]));
433
+        }
434
+
435
+        if (empty($events)) {
436
+            if (!$operation instanceof IComplexOperation) {
437
+                throw new \UnexpectedValueException($this->l->t('No events are chosen.'));
438
+            }
439
+            return;
440
+        }
441
+
442
+        $availableEvents = [];
443
+        foreach ($instance->getEvents() as $event) {
444
+            /** @var IEntityEvent $event */
445
+            $availableEvents[] = $event->getEventName();
446
+        }
447
+
448
+        $diff = array_diff($events, $availableEvents);
449
+        if (!empty($diff)) {
450
+            throw new \UnexpectedValueException($this->l->t('Entity %s has no event %s', [$entity, array_shift($diff)]));
451
+        }
452
+    }
453
+
454
+    /**
455
+     * @param class-string<IOperation> $class
456
+     * @param array<int, Check> $checks
457
+     * @param array $events
458
+     * @throws \UnexpectedValueException
459
+     */
460
+    public function validateOperation(string $class, string $name, array $checks, string $operation, ScopeContext $scope, string $entity, array $events): void {
461
+        try {
462
+            /** @var IOperation $instance */
463
+            $instance = $this->container->get($class);
464
+        } catch (ContainerExceptionInterface $e) {
465
+            throw new \UnexpectedValueException($this->l->t('Operation %s does not exist', [$class]));
466
+        }
467
+
468
+        if (!($instance instanceof IOperation)) {
469
+            throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
470
+        }
471
+
472
+        if (!$instance->isAvailableForScope($scope->getScope())) {
473
+            throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
474
+        }
475
+
476
+        $this->validateEvents($entity, $events, $instance);
477
+
478
+        if (count($checks) === 0) {
479
+            throw new \UnexpectedValueException($this->l->t('At least one check needs to be provided'));
480
+        }
481
+
482
+        if (strlen($operation) > IManager::MAX_OPERATION_VALUE_BYTES) {
483
+            throw new \UnexpectedValueException($this->l->t('The provided operation data is too long'));
484
+        }
485
+
486
+        $instance->validateOperation($name, $checks, $operation);
487
+
488
+        foreach ($checks as $check) {
489
+            if (!is_string($check['class'])) {
490
+                throw new \UnexpectedValueException($this->l->t('Invalid check provided'));
491
+            }
492
+
493
+            try {
494
+                /** @var ICheck $instance */
495
+                $instance = $this->container->get($check['class']);
496
+            } catch (ContainerExceptionInterface) {
497
+                throw new \UnexpectedValueException($this->l->t('Check %s does not exist', [$class]));
498
+            }
499
+
500
+            if (!($instance instanceof ICheck)) {
501
+                throw new \UnexpectedValueException($this->l->t('Check %s is invalid', [$class]));
502
+            }
503
+
504
+            if (!empty($instance->supportedEntities())
505
+                && !in_array($entity, $instance->supportedEntities())
506
+            ) {
507
+                throw new \UnexpectedValueException($this->l->t('Check %s is not allowed with this entity', [$class]));
508
+            }
509
+
510
+            if (strlen((string)$check['value']) > IManager::MAX_CHECK_VALUE_BYTES) {
511
+                throw new \UnexpectedValueException($this->l->t('The provided check value is too long'));
512
+            }
513
+
514
+            $instance->validateCheck($check['operator'], $check['value']);
515
+        }
516
+    }
517
+
518
+    /**
519
+     * @param int[] $checkIds
520
+     * @return array<int, Check>
521
+     */
522
+    public function getChecks(array $checkIds): array {
523
+        $checkIds = array_map('intval', $checkIds);
524
+
525
+        $checks = [];
526
+        foreach ($checkIds as $i => $checkId) {
527
+            if (isset($this->checks[$checkId])) {
528
+                $checks[$checkId] = $this->checks[$checkId];
529
+                unset($checkIds[$i]);
530
+            }
531
+        }
532
+
533
+        if (empty($checkIds)) {
534
+            return $checks;
535
+        }
536
+
537
+        $query = $this->connection->getQueryBuilder();
538
+        $query->select('*')
539
+            ->from('flow_checks')
540
+            ->where($query->expr()->in('id', $query->createNamedParameter($checkIds, IQueryBuilder::PARAM_INT_ARRAY)));
541
+        $result = $query->executeQuery();
542
+
543
+        while ($row = $result->fetchAssociative()) {
544
+            /** @var Check $row */
545
+            $this->checks[(int)$row['id']] = $row;
546
+            $checks[(int)$row['id']] = $row;
547
+        }
548
+        $result->closeCursor();
549
+
550
+        $checkIds = array_diff($checkIds, array_keys($checks));
551
+
552
+        if (!empty($checkIds)) {
553
+            $missingCheck = array_pop($checkIds);
554
+            throw new \UnexpectedValueException($this->l->t('Check #%s does not exist', (string)$missingCheck));
555
+        }
556
+
557
+        return $checks;
558
+    }
559
+
560
+    /**
561
+     * @return int Check unique ID
562
+     */
563
+    protected function addCheck(string $class, string $operator, string $value): int {
564
+        $hash = md5($class . '::' . $operator . '::' . $value);
565
+
566
+        $query = $this->connection->getQueryBuilder();
567
+        $query->select('id')
568
+            ->from('flow_checks')
569
+            ->where($query->expr()->eq('hash', $query->createNamedParameter($hash)));
570
+        $result = $query->executeQuery();
571
+
572
+        if ($row = $result->fetchAssociative()) {
573
+            $result->closeCursor();
574
+            return (int)$row['id'];
575
+        }
576
+
577
+        $query = $this->connection->getQueryBuilder();
578
+        $query->insert('flow_checks')
579
+            ->values([
580
+                'class' => $query->createNamedParameter($class),
581
+                'operator' => $query->createNamedParameter($operator),
582
+                'value' => $query->createNamedParameter($value),
583
+                'hash' => $query->createNamedParameter($hash),
584
+            ]);
585
+        $query->executeStatement();
586
+
587
+        return $query->getLastInsertId();
588
+    }
589
+
590
+    protected function addScope(int $operationId, ScopeContext $scope): void {
591
+        $query = $this->connection->getQueryBuilder();
592
+
593
+        $insertQuery = $query->insert('flow_operations_scope');
594
+        $insertQuery->values([
595
+            'operation_id' => $query->createNamedParameter($operationId),
596
+            'type' => $query->createNamedParameter($scope->getScope()),
597
+            'value' => $query->createNamedParameter($scope->getScopeId()),
598
+        ]);
599
+        $insertQuery->executeStatement();
600
+    }
601
+
602
+    public function formatOperation(array $operation): array {
603
+        $checkIds = json_decode($operation['checks'], true);
604
+        $checks = $this->getChecks($checkIds);
605
+
606
+        $operation['checks'] = [];
607
+        foreach ($checks as $check) {
608
+            // Remove internal values
609
+            unset($check['id']);
610
+            unset($check['hash']);
611
+
612
+            $operation['checks'][] = $check;
613
+        }
614
+        $operation['events'] = json_decode($operation['events'], true) ?? [];
615
+
616
+
617
+        return $operation;
618
+    }
619
+
620
+    /**
621
+     * @return IEntity[]
622
+     */
623
+    public function getEntitiesList(): array {
624
+        $this->dispatcher->dispatchTyped(new RegisterEntitiesEvent($this));
625
+
626
+        return array_values(array_merge($this->getBuildInEntities(), $this->registeredEntities));
627
+    }
628
+
629
+    /**
630
+     * @return IOperation[]
631
+     */
632
+    public function getOperatorList(): array {
633
+        $this->dispatcher->dispatchTyped(new RegisterOperationsEvent($this));
634
+
635
+        return array_merge($this->getBuildInOperators(), $this->registeredOperators);
636
+    }
637
+
638
+    /**
639
+     * @return ICheck[]
640
+     */
641
+    public function getCheckList(): array {
642
+        $this->dispatcher->dispatchTyped(new RegisterChecksEvent($this));
643
+
644
+        return array_merge($this->getBuildInChecks(), $this->registeredChecks);
645
+    }
646
+
647
+    public function registerEntity(IEntity $entity): void {
648
+        $this->registeredEntities[get_class($entity)] = $entity;
649
+    }
650
+
651
+    public function registerOperation(IOperation $operator): void {
652
+        $this->registeredOperators[get_class($operator)] = $operator;
653
+    }
654
+
655
+    public function registerCheck(ICheck $check): void {
656
+        $this->registeredChecks[get_class($check)] = $check;
657
+    }
658
+
659
+    /**
660
+     * @return IEntity[]
661
+     */
662
+    protected function getBuildInEntities(): array {
663
+        try {
664
+            return [
665
+                File::class => $this->container->get(File::class),
666
+            ];
667
+        } catch (ContainerExceptionInterface $e) {
668
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
669
+            return [];
670
+        }
671
+    }
672
+
673
+    /**
674
+     * @return IOperation[]
675
+     */
676
+    protected function getBuildInOperators(): array {
677
+        try {
678
+            return [
679
+                // None yet
680
+            ];
681
+        } catch (ContainerExceptionInterface $e) {
682
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
683
+            return [];
684
+        }
685
+    }
686
+
687
+    /**
688
+     * @return ICheck[]
689
+     */
690
+    protected function getBuildInChecks(): array {
691
+        try {
692
+            return [
693
+                $this->container->get(FileMimeType::class),
694
+                $this->container->get(FileName::class),
695
+                $this->container->get(FileSize::class),
696
+                $this->container->get(FileSystemTags::class),
697
+                $this->container->get(RequestRemoteAddress::class),
698
+                $this->container->get(RequestTime::class),
699
+                $this->container->get(RequestURL::class),
700
+                $this->container->get(RequestUserAgent::class),
701
+                $this->container->get(UserGroupMembership::class),
702
+            ];
703
+        } catch (ContainerExceptionInterface $e) {
704
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
705
+            return [];
706
+        }
707
+    }
708
+
709
+    public function isUserScopeEnabled(): bool {
710
+        return !$this->appConfig->getAppValueBool('user_scope_disabled');
711
+    }
712 712
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -155,7 +155,7 @@  discard block
 block discarded – undo
155 155
 		while ($row = $result->fetchAssociative()) {
156 156
 			$scope = new ScopeContext($row['type'], $row['value']);
157 157
 
158
-			if (!$operation->isAvailableForScope((int)$row['type'])) {
158
+			if (!$operation->isAvailableForScope((int) $row['type'])) {
159 159
 				continue;
160 160
 			}
161 161
 
@@ -195,7 +195,7 @@  discard block
 block discarded – undo
195 195
 				continue;
196 196
 			}
197 197
 
198
-			if (!$operation->isAvailableForScope((int)$row['scope_type'])) {
198
+			if (!$operation->isAvailableForScope((int) $row['scope_type'])) {
199 199
 				continue;
200 200
 			}
201 201
 
@@ -321,7 +321,7 @@  discard block
 block discarded – undo
321 321
 
322 322
 		$operations = [];
323 323
 		while (($opId = $result->fetchOne()) !== false) {
324
-			$operations[] = (int)$opId;
324
+			$operations[] = (int) $opId;
325 325
 		}
326 326
 		$this->operationsByScope[$scopeContext->getHash()] = $operations;
327 327
 		$result->closeCursor();
@@ -393,12 +393,12 @@  discard block
 block discarded – undo
393 393
 		$query = $this->connection->getQueryBuilder();
394 394
 		try {
395 395
 			$this->connection->beginTransaction();
396
-			$result = (bool)$query->delete('flow_operations')
396
+			$result = (bool) $query->delete('flow_operations')
397 397
 				->where($query->expr()->eq('id', $query->createNamedParameter($id)))
398 398
 				->executeStatement();
399 399
 			if ($result) {
400 400
 				$qb = $this->connection->getQueryBuilder();
401
-				$result = (bool)$qb->delete('flow_operations_scope')
401
+				$result = (bool) $qb->delete('flow_operations_scope')
402 402
 					->where($qb->expr()->eq('operation_id', $qb->createNamedParameter($id)))
403 403
 					->executeStatement();
404 404
 			}
@@ -507,7 +507,7 @@  discard block
 block discarded – undo
507 507
 				throw new \UnexpectedValueException($this->l->t('Check %s is not allowed with this entity', [$class]));
508 508
 			}
509 509
 
510
-			if (strlen((string)$check['value']) > IManager::MAX_CHECK_VALUE_BYTES) {
510
+			if (strlen((string) $check['value']) > IManager::MAX_CHECK_VALUE_BYTES) {
511 511
 				throw new \UnexpectedValueException($this->l->t('The provided check value is too long'));
512 512
 			}
513 513
 
@@ -542,8 +542,8 @@  discard block
 block discarded – undo
542 542
 
543 543
 		while ($row = $result->fetchAssociative()) {
544 544
 			/** @var Check $row */
545
-			$this->checks[(int)$row['id']] = $row;
546
-			$checks[(int)$row['id']] = $row;
545
+			$this->checks[(int) $row['id']] = $row;
546
+			$checks[(int) $row['id']] = $row;
547 547
 		}
548 548
 		$result->closeCursor();
549 549
 
@@ -551,7 +551,7 @@  discard block
 block discarded – undo
551 551
 
552 552
 		if (!empty($checkIds)) {
553 553
 			$missingCheck = array_pop($checkIds);
554
-			throw new \UnexpectedValueException($this->l->t('Check #%s does not exist', (string)$missingCheck));
554
+			throw new \UnexpectedValueException($this->l->t('Check #%s does not exist', (string) $missingCheck));
555 555
 		}
556 556
 
557 557
 		return $checks;
@@ -561,7 +561,7 @@  discard block
 block discarded – undo
561 561
 	 * @return int Check unique ID
562 562
 	 */
563 563
 	protected function addCheck(string $class, string $operator, string $value): int {
564
-		$hash = md5($class . '::' . $operator . '::' . $value);
564
+		$hash = md5($class.'::'.$operator.'::'.$value);
565 565
 
566 566
 		$query = $this->connection->getQueryBuilder();
567 567
 		$query->select('id')
@@ -571,7 +571,7 @@  discard block
 block discarded – undo
571 571
 
572 572
 		if ($row = $result->fetchAssociative()) {
573 573
 			$result->closeCursor();
574
-			return (int)$row['id'];
574
+			return (int) $row['id'];
575 575
 		}
576 576
 
577 577
 		$query = $this->connection->getQueryBuilder();
Please login to merge, or discard this patch.
apps/contactsinteraction/lib/Db/RecentContactMapper.php 1 patch
Indentation   +105 added lines, -105 removed lines patch added patch discarded remove patch
@@ -17,109 +17,109 @@
 block discarded – undo
17 17
  * @template-extends QBMapper<RecentContact>
18 18
  */
19 19
 class RecentContactMapper extends QBMapper {
20
-	public const TABLE_NAME = 'recent_contact';
21
-
22
-	public function __construct(IDBConnection $db) {
23
-		parent::__construct($db, self::TABLE_NAME);
24
-	}
25
-
26
-	/**
27
-	 * @return RecentContact[]
28
-	 */
29
-	public function findAll(string $uid): array {
30
-		$qb = $this->db->getQueryBuilder();
31
-
32
-		$select = $qb
33
-			->select('*')
34
-			->from($this->getTableName())
35
-			->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
36
-
37
-		return $this->findEntities($select);
38
-	}
39
-
40
-	/**
41
-	 * @throws DoesNotExistException
42
-	 */
43
-	public function find(string $uid, int $id): RecentContact {
44
-		$qb = $this->db->getQueryBuilder();
45
-
46
-		$select = $qb
47
-			->select('*')
48
-			->from($this->getTableName())
49
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id, $qb::PARAM_INT)))
50
-			->andWhere($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
51
-
52
-		return $this->findEntity($select);
53
-	}
54
-
55
-	/**
56
-	 * @return RecentContact[]
57
-	 */
58
-	public function findMatch(IUser $user,
59
-		?string $uid,
60
-		?string $email,
61
-		?string $cloudId): array {
62
-		$qb = $this->db->getQueryBuilder();
63
-
64
-		$additionalWheres = [];
65
-		if ($uid !== null) {
66
-			$additionalWheres[] = $qb->expr()->eq('uid', $qb->createNamedParameter($uid));
67
-		}
68
-		if ($email !== null) {
69
-			$additionalWheres[] = $qb->expr()->eq('email', $qb->createNamedParameter($email));
70
-		}
71
-		if ($cloudId !== null) {
72
-			$additionalWheres[] = $qb->expr()->eq('federated_cloud_id', $qb->createNamedParameter($cloudId));
73
-		}
74
-
75
-		$select = $qb
76
-			->select('*')
77
-			->from($this->getTableName())
78
-			->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($user->getUID())));
79
-
80
-		if (!empty($additionalWheres)) {
81
-			$select->andWhere($select->expr()->orX(...$additionalWheres));
82
-		}
83
-		return $this->findEntities($select);
84
-	}
85
-
86
-	public function findLastUpdatedForUserId(string $uid): ?int {
87
-		$qb = $this->db->getQueryBuilder();
88
-
89
-		$select = $qb
90
-			->select('last_contact')
91
-			->from($this->getTableName())
92
-			->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)))
93
-			->orderBy('last_contact', 'DESC')
94
-			->setMaxResults(1);
95
-
96
-		$cursor = $select->executeQuery();
97
-		$row = $cursor->fetchAssociative();
98
-
99
-		if ($row === false) {
100
-			return null;
101
-		}
102
-
103
-		return (int)$row['last_contact'];
104
-	}
105
-
106
-	public function cleanUp(int $olderThan): void {
107
-		$qb = $this->db->getQueryBuilder();
108
-
109
-		$delete = $qb
110
-			->delete($this->getTableName())
111
-			->where($qb->expr()->lt('last_contact', $qb->createNamedParameter($olderThan)));
112
-
113
-		$delete->executeStatement();
114
-	}
115
-
116
-	public function deleteByUserId(string $uid): void {
117
-		$qb = $this->db->getQueryBuilder();
118
-
119
-		$delete = $qb
120
-			->delete($this->getTableName())
121
-			->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
122
-
123
-		$delete->executeStatement();
124
-	}
20
+    public const TABLE_NAME = 'recent_contact';
21
+
22
+    public function __construct(IDBConnection $db) {
23
+        parent::__construct($db, self::TABLE_NAME);
24
+    }
25
+
26
+    /**
27
+     * @return RecentContact[]
28
+     */
29
+    public function findAll(string $uid): array {
30
+        $qb = $this->db->getQueryBuilder();
31
+
32
+        $select = $qb
33
+            ->select('*')
34
+            ->from($this->getTableName())
35
+            ->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
36
+
37
+        return $this->findEntities($select);
38
+    }
39
+
40
+    /**
41
+     * @throws DoesNotExistException
42
+     */
43
+    public function find(string $uid, int $id): RecentContact {
44
+        $qb = $this->db->getQueryBuilder();
45
+
46
+        $select = $qb
47
+            ->select('*')
48
+            ->from($this->getTableName())
49
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, $qb::PARAM_INT)))
50
+            ->andWhere($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
51
+
52
+        return $this->findEntity($select);
53
+    }
54
+
55
+    /**
56
+     * @return RecentContact[]
57
+     */
58
+    public function findMatch(IUser $user,
59
+        ?string $uid,
60
+        ?string $email,
61
+        ?string $cloudId): array {
62
+        $qb = $this->db->getQueryBuilder();
63
+
64
+        $additionalWheres = [];
65
+        if ($uid !== null) {
66
+            $additionalWheres[] = $qb->expr()->eq('uid', $qb->createNamedParameter($uid));
67
+        }
68
+        if ($email !== null) {
69
+            $additionalWheres[] = $qb->expr()->eq('email', $qb->createNamedParameter($email));
70
+        }
71
+        if ($cloudId !== null) {
72
+            $additionalWheres[] = $qb->expr()->eq('federated_cloud_id', $qb->createNamedParameter($cloudId));
73
+        }
74
+
75
+        $select = $qb
76
+            ->select('*')
77
+            ->from($this->getTableName())
78
+            ->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($user->getUID())));
79
+
80
+        if (!empty($additionalWheres)) {
81
+            $select->andWhere($select->expr()->orX(...$additionalWheres));
82
+        }
83
+        return $this->findEntities($select);
84
+    }
85
+
86
+    public function findLastUpdatedForUserId(string $uid): ?int {
87
+        $qb = $this->db->getQueryBuilder();
88
+
89
+        $select = $qb
90
+            ->select('last_contact')
91
+            ->from($this->getTableName())
92
+            ->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)))
93
+            ->orderBy('last_contact', 'DESC')
94
+            ->setMaxResults(1);
95
+
96
+        $cursor = $select->executeQuery();
97
+        $row = $cursor->fetchAssociative();
98
+
99
+        if ($row === false) {
100
+            return null;
101
+        }
102
+
103
+        return (int)$row['last_contact'];
104
+    }
105
+
106
+    public function cleanUp(int $olderThan): void {
107
+        $qb = $this->db->getQueryBuilder();
108
+
109
+        $delete = $qb
110
+            ->delete($this->getTableName())
111
+            ->where($qb->expr()->lt('last_contact', $qb->createNamedParameter($olderThan)));
112
+
113
+        $delete->executeStatement();
114
+    }
115
+
116
+    public function deleteByUserId(string $uid): void {
117
+        $qb = $this->db->getQueryBuilder();
118
+
119
+        $delete = $qb
120
+            ->delete($this->getTableName())
121
+            ->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
122
+
123
+        $delete->executeStatement();
124
+    }
125 125
 }
Please login to merge, or discard this patch.
apps/contactsinteraction/lib/Migration/FixVcardCategory.php 1 patch
Indentation   +65 added lines, -65 removed lines patch added patch discarded remove patch
@@ -20,69 +20,69 @@
 block discarded – undo
20 20
 
21 21
 class FixVcardCategory implements IRepairStep {
22 22
 
23
-	private const CARDS_PER_BATCH = 5000;
24
-
25
-	public function __construct(
26
-		private readonly IDBConnection $connection,
27
-		private readonly IJobList $jobList,
28
-	) {
29
-	}
30
-
31
-	public function getName(): string {
32
-		return 'Fix category of recent contacts vcards';
33
-	}
34
-
35
-	public function run(IOutput $output): void {
36
-		$query = $this->connection->getQueryBuilder();
37
-
38
-		$cardsWithTranslatedCategory = $query->select(['id', 'card'])
39
-			->from('recent_contact')
40
-			->where($query->expr()->notLike(
41
-				'card',
42
-				$query->createNamedParameter('%CATEGORIES:Recently contacted%')
43
-			))
44
-			->setMaxResults(self::CARDS_PER_BATCH)
45
-			->executeQuery();
46
-		$rowCount = $cardsWithTranslatedCategory->rowCount();
47
-
48
-		$output->startProgress($rowCount);
49
-
50
-		$this->connection->beginTransaction();
51
-
52
-		$updateQuery = $query->update('recent_contact')
53
-			->set('card', $query->createParameter('card'))
54
-			->where($query->expr()->eq('id', $query->createParameter('id')));
55
-
56
-		while ($card = $cardsWithTranslatedCategory->fetchAssociative()) {
57
-			$output->advance(1);
58
-
59
-			try {
60
-				$vcard = Reader::read($card['card']);
61
-			} catch (ParseException $e) {
62
-				$output->info('Could not parse vcard with id ' . $card['id']);
63
-				continue;
64
-			}
65
-
66
-			$vcard->remove('CATEGORIES');
67
-			$vcard->add('CATEGORIES', 'Recently contacted');
68
-
69
-			$updateQuery->setParameter('id', $card['id']);
70
-			$updateQuery->setParameter('card', $vcard->serialize());
71
-			$updateQuery->executeStatement();
72
-		}
73
-
74
-		$this->connection->commit();
75
-
76
-		$cardsWithTranslatedCategory->closeCursor();
77
-
78
-		$output->finishProgress();
79
-
80
-		if ($rowCount === self::CARDS_PER_BATCH) {
81
-			$this->jobList->add(BackgroundRepair::class, [
82
-				'app' => Application::APP_ID,
83
-				'step' => FixVcardCategory::class,
84
-				'reschedule' => time(), // Use a different argument to reschedule the job
85
-			]);
86
-		}
87
-	}
23
+    private const CARDS_PER_BATCH = 5000;
24
+
25
+    public function __construct(
26
+        private readonly IDBConnection $connection,
27
+        private readonly IJobList $jobList,
28
+    ) {
29
+    }
30
+
31
+    public function getName(): string {
32
+        return 'Fix category of recent contacts vcards';
33
+    }
34
+
35
+    public function run(IOutput $output): void {
36
+        $query = $this->connection->getQueryBuilder();
37
+
38
+        $cardsWithTranslatedCategory = $query->select(['id', 'card'])
39
+            ->from('recent_contact')
40
+            ->where($query->expr()->notLike(
41
+                'card',
42
+                $query->createNamedParameter('%CATEGORIES:Recently contacted%')
43
+            ))
44
+            ->setMaxResults(self::CARDS_PER_BATCH)
45
+            ->executeQuery();
46
+        $rowCount = $cardsWithTranslatedCategory->rowCount();
47
+
48
+        $output->startProgress($rowCount);
49
+
50
+        $this->connection->beginTransaction();
51
+
52
+        $updateQuery = $query->update('recent_contact')
53
+            ->set('card', $query->createParameter('card'))
54
+            ->where($query->expr()->eq('id', $query->createParameter('id')));
55
+
56
+        while ($card = $cardsWithTranslatedCategory->fetchAssociative()) {
57
+            $output->advance(1);
58
+
59
+            try {
60
+                $vcard = Reader::read($card['card']);
61
+            } catch (ParseException $e) {
62
+                $output->info('Could not parse vcard with id ' . $card['id']);
63
+                continue;
64
+            }
65
+
66
+            $vcard->remove('CATEGORIES');
67
+            $vcard->add('CATEGORIES', 'Recently contacted');
68
+
69
+            $updateQuery->setParameter('id', $card['id']);
70
+            $updateQuery->setParameter('card', $vcard->serialize());
71
+            $updateQuery->executeStatement();
72
+        }
73
+
74
+        $this->connection->commit();
75
+
76
+        $cardsWithTranslatedCategory->closeCursor();
77
+
78
+        $output->finishProgress();
79
+
80
+        if ($rowCount === self::CARDS_PER_BATCH) {
81
+            $this->jobList->add(BackgroundRepair::class, [
82
+                'app' => Application::APP_ID,
83
+                'step' => FixVcardCategory::class,
84
+                'reschedule' => time(), // Use a different argument to reschedule the job
85
+            ]);
86
+        }
87
+    }
88 88
 }
Please login to merge, or discard this patch.
apps/federation/tests/DbHandlerTest.php 2 patches
Indentation   +262 added lines, -262 removed lines patch added patch discarded remove patch
@@ -18,266 +18,266 @@
 block discarded – undo
18 18
 
19 19
 #[\PHPUnit\Framework\Attributes\Group('DB')]
20 20
 class DbHandlerTest extends TestCase {
21
-	private DbHandler $dbHandler;
22
-	private IL10N&MockObject $il10n;
23
-	private IDBConnection $connection;
24
-	private string $dbTable = 'trusted_servers';
25
-
26
-	protected function setUp(): void {
27
-		parent::setUp();
28
-
29
-		$this->connection = Server::get(IDBConnection::class);
30
-		$this->il10n = $this->createMock(IL10N::class);
31
-
32
-		$this->dbHandler = new DbHandler(
33
-			$this->connection,
34
-			$this->il10n
35
-		);
36
-
37
-		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
38
-
39
-		$qResult = $query->executeQuery();
40
-		$result = $qResult->fetchAllAssociative();
41
-		$qResult->closeCursor();
42
-		$this->assertEmpty($result, 'we need to start with a empty trusted_servers table');
43
-	}
44
-
45
-	protected function tearDown(): void {
46
-		$query = $this->connection->getQueryBuilder()->delete($this->dbTable);
47
-		$query->executeStatement()
48
-		;
49
-		parent::tearDown();
50
-	}
51
-
52
-	/**
53
-	 *
54
-	 * @param string $url passed to the method
55
-	 * @param string $expectedUrl the url we expect to be written to the db
56
-	 * @param string $expectedHash the hash value we expect to be written to the db
57
-	 */
58
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataTestAddServer')]
59
-	public function testAddServer(string $url, string $expectedUrl, string $expectedHash): void {
60
-		$id = $this->dbHandler->addServer($url);
61
-
62
-		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
63
-
64
-		$qResult = $query->executeQuery();
65
-		$result = $qResult->fetchAllAssociative();
66
-		$qResult->closeCursor();
67
-		$this->assertCount(1, $result);
68
-		$this->assertSame($expectedUrl, $result[0]['url']);
69
-		$this->assertSame($id, (int)$result[0]['id']);
70
-		$this->assertSame($expectedHash, $result[0]['url_hash']);
71
-		$this->assertSame(TrustedServers::STATUS_PENDING, (int)$result[0]['status']);
72
-	}
73
-
74
-	public static function dataTestAddServer(): array {
75
-		return [
76
-			['http://owncloud.org', 'http://owncloud.org', sha1('owncloud.org')],
77
-			['https://owncloud.org', 'https://owncloud.org', sha1('owncloud.org')],
78
-			['http://owncloud.org/', 'http://owncloud.org', sha1('owncloud.org')],
79
-		];
80
-	}
81
-
82
-	public function testRemove(): void {
83
-		$id1 = $this->dbHandler->addServer('server1');
84
-		$id2 = $this->dbHandler->addServer('server2');
85
-
86
-		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
87
-
88
-		$qResult = $query->executeQuery();
89
-		$result = $qResult->fetchAllAssociative();
90
-		$qResult->closeCursor();
91
-		$this->assertCount(2, $result);
92
-		$this->assertSame('server1', $result[0]['url']);
93
-		$this->assertSame('server2', $result[1]['url']);
94
-		$this->assertSame($id1, (int)$result[0]['id']);
95
-		$this->assertSame($id2, (int)$result[1]['id']);
96
-
97
-		$this->dbHandler->removeServer($id2);
98
-		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
99
-
100
-		$qResult = $query->executeQuery();
101
-		$result = $qResult->fetchAllAssociative();
102
-		$qResult->closeCursor();
103
-		$this->assertCount(1, $result);
104
-		$this->assertSame('server1', $result[0]['url']);
105
-		$this->assertSame($id1, (int)$result[0]['id']);
106
-	}
107
-
108
-
109
-	public function testGetServerById(): void {
110
-		$this->dbHandler->addServer('server1');
111
-		$id = $this->dbHandler->addServer('server2');
112
-
113
-		$result = $this->dbHandler->getServerById($id);
114
-		$this->assertSame('server2', $result['url']);
115
-	}
116
-
117
-	public function testGetAll(): void {
118
-		$id1 = $this->dbHandler->addServer('server1');
119
-		$id2 = $this->dbHandler->addServer('server2');
120
-
121
-		$result = $this->dbHandler->getAllServer();
122
-		$this->assertSame(2, count($result));
123
-		$this->assertSame('server1', $result[0]['url']);
124
-		$this->assertSame('server2', $result[1]['url']);
125
-		$this->assertSame($id1, (int)$result[0]['id']);
126
-		$this->assertSame($id2, (int)$result[1]['id']);
127
-	}
128
-
129
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataTestServerExists')]
130
-	public function testServerExists(string $serverInTable, string $checkForServer, bool $expected): void {
131
-		$this->dbHandler->addServer($serverInTable);
132
-		$this->assertSame($expected,
133
-			$this->dbHandler->serverExists($checkForServer)
134
-		);
135
-	}
136
-
137
-	public static function dataTestServerExists(): array {
138
-		return [
139
-			['server1', 'server1', true],
140
-			['server1', 'http://server1', true],
141
-			['server1', 'server2', false]
142
-		];
143
-	}
144
-
145
-	public function XtestAddToken() {
146
-		$this->dbHandler->addServer('server1');
147
-		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
148
-
149
-		$qResult = $query->executeQuery();
150
-		$result = $qResult->fetchAllAssociative();
151
-		$qResult->closeCursor();
152
-		$this->assertCount(1, $result);
153
-		$this->assertSame(null, $result[0]['token']);
154
-		$this->dbHandler->addToken('http://server1', 'token');
155
-		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
156
-
157
-		$qResult = $query->executeQuery();
158
-		$result = $qResult->fetchAllAssociative();
159
-		$qResult->closeCursor();
160
-		$this->assertCount(1, $result);
161
-		$this->assertSame('token', $result[0]['token']);
162
-	}
163
-
164
-	public function testGetToken(): void {
165
-		$this->dbHandler->addServer('server1');
166
-		$this->dbHandler->addToken('http://server1', 'token');
167
-		$this->assertSame('token',
168
-			$this->dbHandler->getToken('https://server1')
169
-		);
170
-	}
171
-
172
-	public function XtestAddSharedSecret() {
173
-		$this->dbHandler->addServer('server1');
174
-		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
175
-
176
-		$qResult = $query->executeQuery();
177
-		$result = $qResult->fetchAllAssociative();
178
-		$qResult->closeCursor();
179
-		$this->assertCount(1, $result);
180
-		$this->assertSame(null, $result[0]['shared_secret']);
181
-		$this->dbHandler->addSharedSecret('http://server1', 'secret');
182
-		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
183
-
184
-		$qResult = $query->executeQuery();
185
-		$result = $qResult->fetchAllAssociative();
186
-		$qResult->closeCursor();
187
-		$this->assertCount(1, $result);
188
-		$this->assertSame('secret', $result[0]['shared_secret']);
189
-	}
190
-
191
-	public function testGetSharedSecret(): void {
192
-		$this->dbHandler->addServer('server1');
193
-		$this->dbHandler->addSharedSecret('http://server1', 'secret');
194
-		$this->assertSame('secret',
195
-			$this->dbHandler->getSharedSecret('https://server1')
196
-		);
197
-	}
198
-
199
-	public function testSetServerStatus(): void {
200
-		$this->dbHandler->addServer('server1');
201
-		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
202
-
203
-		$qResult = $query->executeQuery();
204
-		$result = $qResult->fetchAllAssociative();
205
-		$qResult->closeCursor();
206
-		$this->assertCount(1, $result);
207
-		$this->assertSame(TrustedServers::STATUS_PENDING, (int)$result[0]['status']);
208
-		$this->dbHandler->setServerStatus('http://server1', TrustedServers::STATUS_OK);
209
-		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
210
-
211
-		$qResult = $query->executeQuery();
212
-		$result = $qResult->fetchAllAssociative();
213
-		$qResult->closeCursor();
214
-		$this->assertCount(1, $result);
215
-		$this->assertSame(TrustedServers::STATUS_OK, (int)$result[0]['status']);
216
-	}
217
-
218
-	public function testGetServerStatus(): void {
219
-		$this->dbHandler->addServer('server1');
220
-		$this->dbHandler->setServerStatus('http://server1', TrustedServers::STATUS_OK);
221
-		$this->assertSame(TrustedServers::STATUS_OK,
222
-			$this->dbHandler->getServerStatus('https://server1')
223
-		);
224
-
225
-		// test sync token
226
-		$this->dbHandler->setServerStatus('http://server1', TrustedServers::STATUS_OK, 'token1234567890');
227
-		$servers = $this->dbHandler->getAllServer();
228
-		$this->assertSame('token1234567890', $servers[0]['sync_token']);
229
-	}
230
-
231
-	/**
232
-	 * hash should always be computed with the normalized URL
233
-	 */
234
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataTestHash')]
235
-	public function testHash(string $url, string $expected): void {
236
-		$this->assertSame($expected,
237
-			$this->invokePrivate($this->dbHandler, 'hash', [$url])
238
-		);
239
-	}
240
-
241
-	public static function dataTestHash(): array {
242
-		return [
243
-			['server1', sha1('server1')],
244
-			['http://server1', sha1('server1')],
245
-			['https://server1', sha1('server1')],
246
-			['http://server1/', sha1('server1')],
247
-		];
248
-	}
249
-
250
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataTestNormalizeUrl')]
251
-	public function testNormalizeUrl(string $url, string $expected): void {
252
-		$this->assertSame($expected,
253
-			$this->invokePrivate($this->dbHandler, 'normalizeUrl', [$url])
254
-		);
255
-	}
256
-
257
-	public static function dataTestNormalizeUrl(): array {
258
-		return [
259
-			['owncloud.org', 'owncloud.org'],
260
-			['http://owncloud.org', 'owncloud.org'],
261
-			['https://owncloud.org', 'owncloud.org'],
262
-			['https://owncloud.org//mycloud', 'owncloud.org/mycloud'],
263
-			['https://owncloud.org/mycloud/', 'owncloud.org/mycloud'],
264
-		];
265
-	}
266
-
267
-	#[\PHPUnit\Framework\Attributes\DataProvider('providesAuth')]
268
-	public function testAuth(bool $expectedResult, string $user, string $password): void {
269
-		if ($expectedResult) {
270
-			$this->dbHandler->addServer('url1');
271
-			$this->dbHandler->addSharedSecret('url1', $password);
272
-		}
273
-		$result = $this->dbHandler->auth($user, $password);
274
-		$this->assertEquals($expectedResult, $result);
275
-	}
276
-
277
-	public static function providesAuth(): array {
278
-		return [
279
-			[false, 'foo', ''],
280
-			[true, 'system', '123456789'],
281
-		];
282
-	}
21
+    private DbHandler $dbHandler;
22
+    private IL10N&MockObject $il10n;
23
+    private IDBConnection $connection;
24
+    private string $dbTable = 'trusted_servers';
25
+
26
+    protected function setUp(): void {
27
+        parent::setUp();
28
+
29
+        $this->connection = Server::get(IDBConnection::class);
30
+        $this->il10n = $this->createMock(IL10N::class);
31
+
32
+        $this->dbHandler = new DbHandler(
33
+            $this->connection,
34
+            $this->il10n
35
+        );
36
+
37
+        $query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
38
+
39
+        $qResult = $query->executeQuery();
40
+        $result = $qResult->fetchAllAssociative();
41
+        $qResult->closeCursor();
42
+        $this->assertEmpty($result, 'we need to start with a empty trusted_servers table');
43
+    }
44
+
45
+    protected function tearDown(): void {
46
+        $query = $this->connection->getQueryBuilder()->delete($this->dbTable);
47
+        $query->executeStatement()
48
+        ;
49
+        parent::tearDown();
50
+    }
51
+
52
+    /**
53
+     *
54
+     * @param string $url passed to the method
55
+     * @param string $expectedUrl the url we expect to be written to the db
56
+     * @param string $expectedHash the hash value we expect to be written to the db
57
+     */
58
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataTestAddServer')]
59
+    public function testAddServer(string $url, string $expectedUrl, string $expectedHash): void {
60
+        $id = $this->dbHandler->addServer($url);
61
+
62
+        $query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
63
+
64
+        $qResult = $query->executeQuery();
65
+        $result = $qResult->fetchAllAssociative();
66
+        $qResult->closeCursor();
67
+        $this->assertCount(1, $result);
68
+        $this->assertSame($expectedUrl, $result[0]['url']);
69
+        $this->assertSame($id, (int)$result[0]['id']);
70
+        $this->assertSame($expectedHash, $result[0]['url_hash']);
71
+        $this->assertSame(TrustedServers::STATUS_PENDING, (int)$result[0]['status']);
72
+    }
73
+
74
+    public static function dataTestAddServer(): array {
75
+        return [
76
+            ['http://owncloud.org', 'http://owncloud.org', sha1('owncloud.org')],
77
+            ['https://owncloud.org', 'https://owncloud.org', sha1('owncloud.org')],
78
+            ['http://owncloud.org/', 'http://owncloud.org', sha1('owncloud.org')],
79
+        ];
80
+    }
81
+
82
+    public function testRemove(): void {
83
+        $id1 = $this->dbHandler->addServer('server1');
84
+        $id2 = $this->dbHandler->addServer('server2');
85
+
86
+        $query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
87
+
88
+        $qResult = $query->executeQuery();
89
+        $result = $qResult->fetchAllAssociative();
90
+        $qResult->closeCursor();
91
+        $this->assertCount(2, $result);
92
+        $this->assertSame('server1', $result[0]['url']);
93
+        $this->assertSame('server2', $result[1]['url']);
94
+        $this->assertSame($id1, (int)$result[0]['id']);
95
+        $this->assertSame($id2, (int)$result[1]['id']);
96
+
97
+        $this->dbHandler->removeServer($id2);
98
+        $query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
99
+
100
+        $qResult = $query->executeQuery();
101
+        $result = $qResult->fetchAllAssociative();
102
+        $qResult->closeCursor();
103
+        $this->assertCount(1, $result);
104
+        $this->assertSame('server1', $result[0]['url']);
105
+        $this->assertSame($id1, (int)$result[0]['id']);
106
+    }
107
+
108
+
109
+    public function testGetServerById(): void {
110
+        $this->dbHandler->addServer('server1');
111
+        $id = $this->dbHandler->addServer('server2');
112
+
113
+        $result = $this->dbHandler->getServerById($id);
114
+        $this->assertSame('server2', $result['url']);
115
+    }
116
+
117
+    public function testGetAll(): void {
118
+        $id1 = $this->dbHandler->addServer('server1');
119
+        $id2 = $this->dbHandler->addServer('server2');
120
+
121
+        $result = $this->dbHandler->getAllServer();
122
+        $this->assertSame(2, count($result));
123
+        $this->assertSame('server1', $result[0]['url']);
124
+        $this->assertSame('server2', $result[1]['url']);
125
+        $this->assertSame($id1, (int)$result[0]['id']);
126
+        $this->assertSame($id2, (int)$result[1]['id']);
127
+    }
128
+
129
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataTestServerExists')]
130
+    public function testServerExists(string $serverInTable, string $checkForServer, bool $expected): void {
131
+        $this->dbHandler->addServer($serverInTable);
132
+        $this->assertSame($expected,
133
+            $this->dbHandler->serverExists($checkForServer)
134
+        );
135
+    }
136
+
137
+    public static function dataTestServerExists(): array {
138
+        return [
139
+            ['server1', 'server1', true],
140
+            ['server1', 'http://server1', true],
141
+            ['server1', 'server2', false]
142
+        ];
143
+    }
144
+
145
+    public function XtestAddToken() {
146
+        $this->dbHandler->addServer('server1');
147
+        $query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
148
+
149
+        $qResult = $query->executeQuery();
150
+        $result = $qResult->fetchAllAssociative();
151
+        $qResult->closeCursor();
152
+        $this->assertCount(1, $result);
153
+        $this->assertSame(null, $result[0]['token']);
154
+        $this->dbHandler->addToken('http://server1', 'token');
155
+        $query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
156
+
157
+        $qResult = $query->executeQuery();
158
+        $result = $qResult->fetchAllAssociative();
159
+        $qResult->closeCursor();
160
+        $this->assertCount(1, $result);
161
+        $this->assertSame('token', $result[0]['token']);
162
+    }
163
+
164
+    public function testGetToken(): void {
165
+        $this->dbHandler->addServer('server1');
166
+        $this->dbHandler->addToken('http://server1', 'token');
167
+        $this->assertSame('token',
168
+            $this->dbHandler->getToken('https://server1')
169
+        );
170
+    }
171
+
172
+    public function XtestAddSharedSecret() {
173
+        $this->dbHandler->addServer('server1');
174
+        $query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
175
+
176
+        $qResult = $query->executeQuery();
177
+        $result = $qResult->fetchAllAssociative();
178
+        $qResult->closeCursor();
179
+        $this->assertCount(1, $result);
180
+        $this->assertSame(null, $result[0]['shared_secret']);
181
+        $this->dbHandler->addSharedSecret('http://server1', 'secret');
182
+        $query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
183
+
184
+        $qResult = $query->executeQuery();
185
+        $result = $qResult->fetchAllAssociative();
186
+        $qResult->closeCursor();
187
+        $this->assertCount(1, $result);
188
+        $this->assertSame('secret', $result[0]['shared_secret']);
189
+    }
190
+
191
+    public function testGetSharedSecret(): void {
192
+        $this->dbHandler->addServer('server1');
193
+        $this->dbHandler->addSharedSecret('http://server1', 'secret');
194
+        $this->assertSame('secret',
195
+            $this->dbHandler->getSharedSecret('https://server1')
196
+        );
197
+    }
198
+
199
+    public function testSetServerStatus(): void {
200
+        $this->dbHandler->addServer('server1');
201
+        $query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
202
+
203
+        $qResult = $query->executeQuery();
204
+        $result = $qResult->fetchAllAssociative();
205
+        $qResult->closeCursor();
206
+        $this->assertCount(1, $result);
207
+        $this->assertSame(TrustedServers::STATUS_PENDING, (int)$result[0]['status']);
208
+        $this->dbHandler->setServerStatus('http://server1', TrustedServers::STATUS_OK);
209
+        $query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
210
+
211
+        $qResult = $query->executeQuery();
212
+        $result = $qResult->fetchAllAssociative();
213
+        $qResult->closeCursor();
214
+        $this->assertCount(1, $result);
215
+        $this->assertSame(TrustedServers::STATUS_OK, (int)$result[0]['status']);
216
+    }
217
+
218
+    public function testGetServerStatus(): void {
219
+        $this->dbHandler->addServer('server1');
220
+        $this->dbHandler->setServerStatus('http://server1', TrustedServers::STATUS_OK);
221
+        $this->assertSame(TrustedServers::STATUS_OK,
222
+            $this->dbHandler->getServerStatus('https://server1')
223
+        );
224
+
225
+        // test sync token
226
+        $this->dbHandler->setServerStatus('http://server1', TrustedServers::STATUS_OK, 'token1234567890');
227
+        $servers = $this->dbHandler->getAllServer();
228
+        $this->assertSame('token1234567890', $servers[0]['sync_token']);
229
+    }
230
+
231
+    /**
232
+     * hash should always be computed with the normalized URL
233
+     */
234
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataTestHash')]
235
+    public function testHash(string $url, string $expected): void {
236
+        $this->assertSame($expected,
237
+            $this->invokePrivate($this->dbHandler, 'hash', [$url])
238
+        );
239
+    }
240
+
241
+    public static function dataTestHash(): array {
242
+        return [
243
+            ['server1', sha1('server1')],
244
+            ['http://server1', sha1('server1')],
245
+            ['https://server1', sha1('server1')],
246
+            ['http://server1/', sha1('server1')],
247
+        ];
248
+    }
249
+
250
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataTestNormalizeUrl')]
251
+    public function testNormalizeUrl(string $url, string $expected): void {
252
+        $this->assertSame($expected,
253
+            $this->invokePrivate($this->dbHandler, 'normalizeUrl', [$url])
254
+        );
255
+    }
256
+
257
+    public static function dataTestNormalizeUrl(): array {
258
+        return [
259
+            ['owncloud.org', 'owncloud.org'],
260
+            ['http://owncloud.org', 'owncloud.org'],
261
+            ['https://owncloud.org', 'owncloud.org'],
262
+            ['https://owncloud.org//mycloud', 'owncloud.org/mycloud'],
263
+            ['https://owncloud.org/mycloud/', 'owncloud.org/mycloud'],
264
+        ];
265
+    }
266
+
267
+    #[\PHPUnit\Framework\Attributes\DataProvider('providesAuth')]
268
+    public function testAuth(bool $expectedResult, string $user, string $password): void {
269
+        if ($expectedResult) {
270
+            $this->dbHandler->addServer('url1');
271
+            $this->dbHandler->addSharedSecret('url1', $password);
272
+        }
273
+        $result = $this->dbHandler->auth($user, $password);
274
+        $this->assertEquals($expectedResult, $result);
275
+    }
276
+
277
+    public static function providesAuth(): array {
278
+        return [
279
+            [false, 'foo', ''],
280
+            [true, 'system', '123456789'],
281
+        ];
282
+    }
283 283
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -66,9 +66,9 @@  discard block
 block discarded – undo
66 66
 		$qResult->closeCursor();
67 67
 		$this->assertCount(1, $result);
68 68
 		$this->assertSame($expectedUrl, $result[0]['url']);
69
-		$this->assertSame($id, (int)$result[0]['id']);
69
+		$this->assertSame($id, (int) $result[0]['id']);
70 70
 		$this->assertSame($expectedHash, $result[0]['url_hash']);
71
-		$this->assertSame(TrustedServers::STATUS_PENDING, (int)$result[0]['status']);
71
+		$this->assertSame(TrustedServers::STATUS_PENDING, (int) $result[0]['status']);
72 72
 	}
73 73
 
74 74
 	public static function dataTestAddServer(): array {
@@ -91,8 +91,8 @@  discard block
 block discarded – undo
91 91
 		$this->assertCount(2, $result);
92 92
 		$this->assertSame('server1', $result[0]['url']);
93 93
 		$this->assertSame('server2', $result[1]['url']);
94
-		$this->assertSame($id1, (int)$result[0]['id']);
95
-		$this->assertSame($id2, (int)$result[1]['id']);
94
+		$this->assertSame($id1, (int) $result[0]['id']);
95
+		$this->assertSame($id2, (int) $result[1]['id']);
96 96
 
97 97
 		$this->dbHandler->removeServer($id2);
98 98
 		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
@@ -102,7 +102,7 @@  discard block
 block discarded – undo
102 102
 		$qResult->closeCursor();
103 103
 		$this->assertCount(1, $result);
104 104
 		$this->assertSame('server1', $result[0]['url']);
105
-		$this->assertSame($id1, (int)$result[0]['id']);
105
+		$this->assertSame($id1, (int) $result[0]['id']);
106 106
 	}
107 107
 
108 108
 
@@ -122,8 +122,8 @@  discard block
 block discarded – undo
122 122
 		$this->assertSame(2, count($result));
123 123
 		$this->assertSame('server1', $result[0]['url']);
124 124
 		$this->assertSame('server2', $result[1]['url']);
125
-		$this->assertSame($id1, (int)$result[0]['id']);
126
-		$this->assertSame($id2, (int)$result[1]['id']);
125
+		$this->assertSame($id1, (int) $result[0]['id']);
126
+		$this->assertSame($id2, (int) $result[1]['id']);
127 127
 	}
128 128
 
129 129
 	#[\PHPUnit\Framework\Attributes\DataProvider('dataTestServerExists')]
@@ -204,7 +204,7 @@  discard block
 block discarded – undo
204 204
 		$result = $qResult->fetchAllAssociative();
205 205
 		$qResult->closeCursor();
206 206
 		$this->assertCount(1, $result);
207
-		$this->assertSame(TrustedServers::STATUS_PENDING, (int)$result[0]['status']);
207
+		$this->assertSame(TrustedServers::STATUS_PENDING, (int) $result[0]['status']);
208 208
 		$this->dbHandler->setServerStatus('http://server1', TrustedServers::STATUS_OK);
209 209
 		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
210 210
 
@@ -212,7 +212,7 @@  discard block
 block discarded – undo
212 212
 		$result = $qResult->fetchAllAssociative();
213 213
 		$qResult->closeCursor();
214 214
 		$this->assertCount(1, $result);
215
-		$this->assertSame(TrustedServers::STATUS_OK, (int)$result[0]['status']);
215
+		$this->assertSame(TrustedServers::STATUS_OK, (int) $result[0]['status']);
216 216
 	}
217 217
 
218 218
 	public function testGetServerStatus(): void {
Please login to merge, or discard this patch.
apps/federation/lib/DbHandler.php 2 patches
Indentation   +241 added lines, -241 removed lines patch added patch discarded remove patch
@@ -23,245 +23,245 @@
 block discarded – undo
23 23
  * @package OCA\Federation
24 24
  */
25 25
 class DbHandler {
26
-	private string $dbTable = 'trusted_servers';
27
-
28
-	public function __construct(
29
-		private IDBConnection $connection,
30
-		private IL10N $IL10N,
31
-	) {
32
-	}
33
-
34
-	/**
35
-	 * Add server to the list of trusted servers
36
-	 *
37
-	 * @throws HintException
38
-	 */
39
-	public function addServer(string $url): int {
40
-		$hash = $this->hash($url);
41
-		$url = rtrim($url, '/');
42
-		$query = $this->connection->getQueryBuilder();
43
-		$query->insert($this->dbTable)
44
-			->values([
45
-				'url' => $query->createParameter('url'),
46
-				'url_hash' => $query->createParameter('url_hash'),
47
-			])
48
-			->setParameter('url', $url)
49
-			->setParameter('url_hash', $hash);
50
-
51
-		$result = $query->executeStatement();
52
-
53
-		if ($result) {
54
-			return $query->getLastInsertId();
55
-		}
56
-
57
-		$message = 'Internal failure, Could not add trusted server: ' . $url;
58
-		$message_t = $this->IL10N->t('Could not add server');
59
-		throw new HintException($message, $message_t);
60
-		return -1;
61
-	}
62
-
63
-	/**
64
-	 * Remove server from the list of trusted servers
65
-	 */
66
-	public function removeServer(int $id): void {
67
-		$query = $this->connection->getQueryBuilder();
68
-		$query->delete($this->dbTable)
69
-			->where($query->expr()->eq('id', $query->createParameter('id')))
70
-			->setParameter('id', $id);
71
-		$query->executeStatement();
72
-	}
73
-
74
-	/**
75
-	 * Get trusted server with given ID
76
-	 *
77
-	 * @return array{id: int, url: string, url_hash: string, token: ?string, shared_secret: ?string, status: int, sync_token: ?string}
78
-	 * @throws \Exception
79
-	 */
80
-	public function getServerById(int $id): array {
81
-		$query = $this->connection->getQueryBuilder();
82
-		$query->select('*')->from($this->dbTable)
83
-			->where($query->expr()->eq('id', $query->createParameter('id')))
84
-			->setParameter('id', $id, IQueryBuilder::PARAM_INT);
85
-
86
-		$qResult = $query->executeQuery();
87
-		/** @var array{id: int, url: string, url_hash: string, token: ?string, shared_secret: ?string, status: int, sync_token: ?string} $result */
88
-		$result = $qResult->fetchAssociative();
89
-		$qResult->closeCursor();
90
-
91
-		if ($result === false) {
92
-			throw new \Exception('No Server found with ID: ' . $id);
93
-		}
94
-
95
-		return $result;
96
-	}
97
-
98
-	/**
99
-	 * Get all trusted servers
100
-	 *
101
-	 * @return list<array{id: int, url: string, url_hash: string, shared_secret: ?string, status: int, sync_token: ?string}>
102
-	 * @throws DBException
103
-	 */
104
-	public function getAllServer(): array {
105
-		$query = $this->connection->getQueryBuilder();
106
-		$query->select(['url', 'url_hash', 'id', 'status', 'shared_secret', 'sync_token'])
107
-			->from($this->dbTable);
108
-		$statement = $query->executeQuery();
109
-		$result = $statement->fetchAllAssociative();
110
-		$statement->closeCursor();
111
-		return $result;
112
-	}
113
-
114
-	/**
115
-	 * Check if server already exists in the database table
116
-	 */
117
-	public function serverExists(string $url): bool {
118
-		$hash = $this->hash($url);
119
-		$query = $this->connection->getQueryBuilder();
120
-		$query->select('url')
121
-			->from($this->dbTable)
122
-			->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
123
-			->setParameter('url_hash', $hash);
124
-		$statement = $query->executeQuery();
125
-		$result = $statement->fetchAllAssociative();
126
-		$statement->closeCursor();
127
-
128
-		return !empty($result);
129
-	}
130
-
131
-	/**
132
-	 * Write token to database. Token is used to exchange the secret
133
-	 */
134
-	public function addToken(string $url, string $token): void {
135
-		$hash = $this->hash($url);
136
-		$query = $this->connection->getQueryBuilder();
137
-		$query->update($this->dbTable)
138
-			->set('token', $query->createParameter('token'))
139
-			->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
140
-			->setParameter('url_hash', $hash)
141
-			->setParameter('token', $token);
142
-		$query->executeStatement();
143
-	}
144
-
145
-	/**
146
-	 * Get token stored in database
147
-	 * @throws \Exception
148
-	 */
149
-	public function getToken(string $url): string {
150
-		$hash = $this->hash($url);
151
-		$query = $this->connection->getQueryBuilder();
152
-		$query->select('token')->from($this->dbTable)
153
-			->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
154
-			->setParameter('url_hash', $hash);
155
-
156
-		$statement = $query->executeQuery();
157
-		$result = $statement->fetchAssociative();
158
-		$statement->closeCursor();
159
-
160
-		if (!isset($result['token'])) {
161
-			throw new \Exception('No token found for: ' . $url);
162
-		}
163
-
164
-		return $result['token'];
165
-	}
166
-
167
-	/**
168
-	 * Add shared Secret to database
169
-	 */
170
-	public function addSharedSecret(string $url, string $sharedSecret): void {
171
-		$hash = $this->hash($url);
172
-		$query = $this->connection->getQueryBuilder();
173
-		$query->update($this->dbTable)
174
-			->set('shared_secret', $query->createParameter('sharedSecret'))
175
-			->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
176
-			->setParameter('url_hash', $hash)
177
-			->setParameter('sharedSecret', $sharedSecret);
178
-		$query->executeStatement();
179
-	}
180
-
181
-	/**
182
-	 * Get shared secret from database
183
-	 */
184
-	public function getSharedSecret(string $url): string {
185
-		$hash = $this->hash($url);
186
-		$query = $this->connection->getQueryBuilder();
187
-		$query->select('shared_secret')->from($this->dbTable)
188
-			->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
189
-			->setParameter('url_hash', $hash);
190
-
191
-		$statement = $query->executeQuery();
192
-		$result = $statement->fetchAssociative();
193
-		$statement->closeCursor();
194
-		return (string)$result['shared_secret'];
195
-	}
196
-
197
-	/**
198
-	 * Set server status
199
-	 */
200
-	public function setServerStatus(string $url, int $status, ?string $token = null): void {
201
-		$hash = $this->hash($url);
202
-		$query = $this->connection->getQueryBuilder();
203
-		$query->update($this->dbTable)
204
-			->set('status', $query->createNamedParameter($status))
205
-			->where($query->expr()->eq('url_hash', $query->createNamedParameter($hash)));
206
-		if (!is_null($token)) {
207
-			$query->set('sync_token', $query->createNamedParameter($token));
208
-		}
209
-		$query->executeStatement();
210
-	}
211
-
212
-	/**
213
-	 * Get server status
214
-	 */
215
-	public function getServerStatus(string $url): int {
216
-		$hash = $this->hash($url);
217
-		$query = $this->connection->getQueryBuilder();
218
-		$query->select('status')->from($this->dbTable)
219
-			->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
220
-			->setParameter('url_hash', $hash);
221
-
222
-		$statement = $query->executeQuery();
223
-		$result = $statement->fetchAssociative();
224
-		$statement->closeCursor();
225
-		return (int)$result['status'];
226
-	}
227
-
228
-	/**
229
-	 * Create hash from URL
230
-	 */
231
-	protected function hash(string $url): string {
232
-		$normalized = $this->normalizeUrl($url);
233
-		return sha1($normalized);
234
-	}
235
-
236
-	/**
237
-	 * Normalize URL, used to create the sha1 hash
238
-	 */
239
-	protected function normalizeUrl(string $url): string {
240
-		$normalized = $url;
241
-
242
-		if (strpos($url, 'https://') === 0) {
243
-			$normalized = substr($url, strlen('https://'));
244
-		} elseif (strpos($url, 'http://') === 0) {
245
-			$normalized = substr($url, strlen('http://'));
246
-		}
247
-
248
-		$normalized = Filesystem::normalizePath($normalized);
249
-		$normalized = trim($normalized, '/');
250
-
251
-		return $normalized;
252
-	}
253
-
254
-	public function auth(string $username, string $password): bool {
255
-		if ($username !== 'system') {
256
-			return false;
257
-		}
258
-		$query = $this->connection->getQueryBuilder();
259
-		$query->select('url')->from($this->dbTable)
260
-			->where($query->expr()->eq('shared_secret', $query->createNamedParameter($password)));
261
-
262
-		$statement = $query->executeQuery();
263
-		$result = $statement->fetchAssociative();
264
-		$statement->closeCursor();
265
-		return !empty($result);
266
-	}
26
+    private string $dbTable = 'trusted_servers';
27
+
28
+    public function __construct(
29
+        private IDBConnection $connection,
30
+        private IL10N $IL10N,
31
+    ) {
32
+    }
33
+
34
+    /**
35
+     * Add server to the list of trusted servers
36
+     *
37
+     * @throws HintException
38
+     */
39
+    public function addServer(string $url): int {
40
+        $hash = $this->hash($url);
41
+        $url = rtrim($url, '/');
42
+        $query = $this->connection->getQueryBuilder();
43
+        $query->insert($this->dbTable)
44
+            ->values([
45
+                'url' => $query->createParameter('url'),
46
+                'url_hash' => $query->createParameter('url_hash'),
47
+            ])
48
+            ->setParameter('url', $url)
49
+            ->setParameter('url_hash', $hash);
50
+
51
+        $result = $query->executeStatement();
52
+
53
+        if ($result) {
54
+            return $query->getLastInsertId();
55
+        }
56
+
57
+        $message = 'Internal failure, Could not add trusted server: ' . $url;
58
+        $message_t = $this->IL10N->t('Could not add server');
59
+        throw new HintException($message, $message_t);
60
+        return -1;
61
+    }
62
+
63
+    /**
64
+     * Remove server from the list of trusted servers
65
+     */
66
+    public function removeServer(int $id): void {
67
+        $query = $this->connection->getQueryBuilder();
68
+        $query->delete($this->dbTable)
69
+            ->where($query->expr()->eq('id', $query->createParameter('id')))
70
+            ->setParameter('id', $id);
71
+        $query->executeStatement();
72
+    }
73
+
74
+    /**
75
+     * Get trusted server with given ID
76
+     *
77
+     * @return array{id: int, url: string, url_hash: string, token: ?string, shared_secret: ?string, status: int, sync_token: ?string}
78
+     * @throws \Exception
79
+     */
80
+    public function getServerById(int $id): array {
81
+        $query = $this->connection->getQueryBuilder();
82
+        $query->select('*')->from($this->dbTable)
83
+            ->where($query->expr()->eq('id', $query->createParameter('id')))
84
+            ->setParameter('id', $id, IQueryBuilder::PARAM_INT);
85
+
86
+        $qResult = $query->executeQuery();
87
+        /** @var array{id: int, url: string, url_hash: string, token: ?string, shared_secret: ?string, status: int, sync_token: ?string} $result */
88
+        $result = $qResult->fetchAssociative();
89
+        $qResult->closeCursor();
90
+
91
+        if ($result === false) {
92
+            throw new \Exception('No Server found with ID: ' . $id);
93
+        }
94
+
95
+        return $result;
96
+    }
97
+
98
+    /**
99
+     * Get all trusted servers
100
+     *
101
+     * @return list<array{id: int, url: string, url_hash: string, shared_secret: ?string, status: int, sync_token: ?string}>
102
+     * @throws DBException
103
+     */
104
+    public function getAllServer(): array {
105
+        $query = $this->connection->getQueryBuilder();
106
+        $query->select(['url', 'url_hash', 'id', 'status', 'shared_secret', 'sync_token'])
107
+            ->from($this->dbTable);
108
+        $statement = $query->executeQuery();
109
+        $result = $statement->fetchAllAssociative();
110
+        $statement->closeCursor();
111
+        return $result;
112
+    }
113
+
114
+    /**
115
+     * Check if server already exists in the database table
116
+     */
117
+    public function serverExists(string $url): bool {
118
+        $hash = $this->hash($url);
119
+        $query = $this->connection->getQueryBuilder();
120
+        $query->select('url')
121
+            ->from($this->dbTable)
122
+            ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
123
+            ->setParameter('url_hash', $hash);
124
+        $statement = $query->executeQuery();
125
+        $result = $statement->fetchAllAssociative();
126
+        $statement->closeCursor();
127
+
128
+        return !empty($result);
129
+    }
130
+
131
+    /**
132
+     * Write token to database. Token is used to exchange the secret
133
+     */
134
+    public function addToken(string $url, string $token): void {
135
+        $hash = $this->hash($url);
136
+        $query = $this->connection->getQueryBuilder();
137
+        $query->update($this->dbTable)
138
+            ->set('token', $query->createParameter('token'))
139
+            ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
140
+            ->setParameter('url_hash', $hash)
141
+            ->setParameter('token', $token);
142
+        $query->executeStatement();
143
+    }
144
+
145
+    /**
146
+     * Get token stored in database
147
+     * @throws \Exception
148
+     */
149
+    public function getToken(string $url): string {
150
+        $hash = $this->hash($url);
151
+        $query = $this->connection->getQueryBuilder();
152
+        $query->select('token')->from($this->dbTable)
153
+            ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
154
+            ->setParameter('url_hash', $hash);
155
+
156
+        $statement = $query->executeQuery();
157
+        $result = $statement->fetchAssociative();
158
+        $statement->closeCursor();
159
+
160
+        if (!isset($result['token'])) {
161
+            throw new \Exception('No token found for: ' . $url);
162
+        }
163
+
164
+        return $result['token'];
165
+    }
166
+
167
+    /**
168
+     * Add shared Secret to database
169
+     */
170
+    public function addSharedSecret(string $url, string $sharedSecret): void {
171
+        $hash = $this->hash($url);
172
+        $query = $this->connection->getQueryBuilder();
173
+        $query->update($this->dbTable)
174
+            ->set('shared_secret', $query->createParameter('sharedSecret'))
175
+            ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
176
+            ->setParameter('url_hash', $hash)
177
+            ->setParameter('sharedSecret', $sharedSecret);
178
+        $query->executeStatement();
179
+    }
180
+
181
+    /**
182
+     * Get shared secret from database
183
+     */
184
+    public function getSharedSecret(string $url): string {
185
+        $hash = $this->hash($url);
186
+        $query = $this->connection->getQueryBuilder();
187
+        $query->select('shared_secret')->from($this->dbTable)
188
+            ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
189
+            ->setParameter('url_hash', $hash);
190
+
191
+        $statement = $query->executeQuery();
192
+        $result = $statement->fetchAssociative();
193
+        $statement->closeCursor();
194
+        return (string)$result['shared_secret'];
195
+    }
196
+
197
+    /**
198
+     * Set server status
199
+     */
200
+    public function setServerStatus(string $url, int $status, ?string $token = null): void {
201
+        $hash = $this->hash($url);
202
+        $query = $this->connection->getQueryBuilder();
203
+        $query->update($this->dbTable)
204
+            ->set('status', $query->createNamedParameter($status))
205
+            ->where($query->expr()->eq('url_hash', $query->createNamedParameter($hash)));
206
+        if (!is_null($token)) {
207
+            $query->set('sync_token', $query->createNamedParameter($token));
208
+        }
209
+        $query->executeStatement();
210
+    }
211
+
212
+    /**
213
+     * Get server status
214
+     */
215
+    public function getServerStatus(string $url): int {
216
+        $hash = $this->hash($url);
217
+        $query = $this->connection->getQueryBuilder();
218
+        $query->select('status')->from($this->dbTable)
219
+            ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
220
+            ->setParameter('url_hash', $hash);
221
+
222
+        $statement = $query->executeQuery();
223
+        $result = $statement->fetchAssociative();
224
+        $statement->closeCursor();
225
+        return (int)$result['status'];
226
+    }
227
+
228
+    /**
229
+     * Create hash from URL
230
+     */
231
+    protected function hash(string $url): string {
232
+        $normalized = $this->normalizeUrl($url);
233
+        return sha1($normalized);
234
+    }
235
+
236
+    /**
237
+     * Normalize URL, used to create the sha1 hash
238
+     */
239
+    protected function normalizeUrl(string $url): string {
240
+        $normalized = $url;
241
+
242
+        if (strpos($url, 'https://') === 0) {
243
+            $normalized = substr($url, strlen('https://'));
244
+        } elseif (strpos($url, 'http://') === 0) {
245
+            $normalized = substr($url, strlen('http://'));
246
+        }
247
+
248
+        $normalized = Filesystem::normalizePath($normalized);
249
+        $normalized = trim($normalized, '/');
250
+
251
+        return $normalized;
252
+    }
253
+
254
+    public function auth(string $username, string $password): bool {
255
+        if ($username !== 'system') {
256
+            return false;
257
+        }
258
+        $query = $this->connection->getQueryBuilder();
259
+        $query->select('url')->from($this->dbTable)
260
+            ->where($query->expr()->eq('shared_secret', $query->createNamedParameter($password)));
261
+
262
+        $statement = $query->executeQuery();
263
+        $result = $statement->fetchAssociative();
264
+        $statement->closeCursor();
265
+        return !empty($result);
266
+    }
267 267
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -54,7 +54,7 @@  discard block
 block discarded – undo
54 54
 			return $query->getLastInsertId();
55 55
 		}
56 56
 
57
-		$message = 'Internal failure, Could not add trusted server: ' . $url;
57
+		$message = 'Internal failure, Could not add trusted server: '.$url;
58 58
 		$message_t = $this->IL10N->t('Could not add server');
59 59
 		throw new HintException($message, $message_t);
60 60
 		return -1;
@@ -89,7 +89,7 @@  discard block
 block discarded – undo
89 89
 		$qResult->closeCursor();
90 90
 
91 91
 		if ($result === false) {
92
-			throw new \Exception('No Server found with ID: ' . $id);
92
+			throw new \Exception('No Server found with ID: '.$id);
93 93
 		}
94 94
 
95 95
 		return $result;
@@ -158,7 +158,7 @@  discard block
 block discarded – undo
158 158
 		$statement->closeCursor();
159 159
 
160 160
 		if (!isset($result['token'])) {
161
-			throw new \Exception('No token found for: ' . $url);
161
+			throw new \Exception('No token found for: '.$url);
162 162
 		}
163 163
 
164 164
 		return $result['token'];
@@ -191,7 +191,7 @@  discard block
 block discarded – undo
191 191
 		$statement = $query->executeQuery();
192 192
 		$result = $statement->fetchAssociative();
193 193
 		$statement->closeCursor();
194
-		return (string)$result['shared_secret'];
194
+		return (string) $result['shared_secret'];
195 195
 	}
196 196
 
197 197
 	/**
@@ -222,7 +222,7 @@  discard block
 block discarded – undo
222 222
 		$statement = $query->executeQuery();
223 223
 		$result = $statement->fetchAssociative();
224 224
 		$statement->closeCursor();
225
-		return (int)$result['status'];
225
+		return (int) $result['status'];
226 226
 	}
227 227
 
228 228
 	/**
Please login to merge, or discard this patch.
apps/files_sharing/tests/Controller/ShareAPIControllerTest.php 1 patch
Indentation   +5568 added lines, -5568 removed lines patch added patch discarded remove patch
@@ -66,517 +66,517 @@  discard block
 block discarded – undo
66 66
  */
67 67
 #[\PHPUnit\Framework\Attributes\Group('DB')]
68 68
 class ShareAPIControllerTest extends TestCase {
69
-	use EmailValidatorTrait;
70
-
71
-	private string $appName = 'files_sharing';
72
-	private string $currentUser;
73
-
74
-	private ShareAPIController $ocs;
75
-
76
-	private IManager&MockObject $shareManager;
77
-	private IGroupManager&MockObject $groupManager;
78
-	private IUserManager&MockObject $userManager;
79
-	private IRequest&MockObject $request;
80
-	private IRootFolder&MockObject $rootFolder;
81
-	private IURLGenerator&MockObject $urlGenerator;
82
-	private IL10N&MockObject $l;
83
-	private IConfig&MockObject $config;
84
-	private IAppConfig&MockObject $appConfig;
85
-	private IAppManager&MockObject $appManager;
86
-	private ContainerInterface&MockObject $serverContainer;
87
-	private IUserStatusManager&MockObject $userStatusManager;
88
-	private IPreview&MockObject $previewManager;
89
-	private IDateTimeZone&MockObject $dateTimeZone;
90
-	private LoggerInterface&MockObject $logger;
91
-	private IProviderFactory&MockObject $factory;
92
-	private IMailer&MockObject $mailer;
93
-	private ITagManager&MockObject $tagManager;
94
-	private TrustedServers&MockObject $trustedServers;
95
-
96
-	protected function setUp(): void {
97
-		$this->shareManager = $this->createMock(IManager::class);
98
-		$this->shareManager
99
-			->expects($this->any())
100
-			->method('shareApiEnabled')
101
-			->willReturn(true);
102
-		$this->shareManager
103
-			->expects($this->any())
104
-			->method('shareProviderExists')->willReturn(true);
105
-		$this->groupManager = $this->createMock(IGroupManager::class);
106
-		$this->userManager = $this->createMock(IUserManager::class);
107
-		$this->request = $this->createMock(IRequest::class);
108
-		$this->rootFolder = $this->createMock(IRootFolder::class);
109
-		$this->urlGenerator = $this->createMock(IURLGenerator::class);
110
-		$this->currentUser = 'currentUser';
111
-
112
-		$this->l = $this->createMock(IL10N::class);
113
-		$this->l->method('t')
114
-			->willReturnCallback(function ($text, $parameters = []) {
115
-				return vsprintf($text, $parameters);
116
-			});
117
-		$this->config = $this->createMock(IConfig::class);
118
-		$this->appConfig = $this->createMock(IAppConfig::class);
119
-		$this->appManager = $this->createMock(IAppManager::class);
120
-		$this->serverContainer = $this->createMock(ContainerInterface::class);
121
-		$this->userStatusManager = $this->createMock(IUserStatusManager::class);
122
-		$this->previewManager = $this->createMock(IPreview::class);
123
-		$this->previewManager->method('isAvailable')
124
-			->willReturnCallback(function ($fileInfo) {
125
-				return $fileInfo->getMimeType() === 'mimeWithPreview';
126
-			});
127
-		$this->dateTimeZone = $this->createMock(IDateTimeZone::class);
128
-		$this->logger = $this->createMock(LoggerInterface::class);
129
-		$this->factory = $this->createMock(IProviderFactory::class);
130
-		$this->mailer = $this->createMock(IMailer::class);
131
-		$this->tagManager = $this->createMock(ITagManager::class);
132
-		$this->trustedServers = $this->createMock(TrustedServers::class);
133
-
134
-		$this->ocs = new ShareAPIController(
135
-			$this->appName,
136
-			$this->request,
137
-			$this->shareManager,
138
-			$this->groupManager,
139
-			$this->userManager,
140
-			$this->rootFolder,
141
-			$this->urlGenerator,
142
-			$this->l,
143
-			$this->config,
144
-			$this->appConfig,
145
-			$this->appManager,
146
-			$this->serverContainer,
147
-			$this->userStatusManager,
148
-			$this->previewManager,
149
-			$this->dateTimeZone,
150
-			$this->logger,
151
-			$this->factory,
152
-			$this->mailer,
153
-			$this->tagManager,
154
-			$this->getEmailValidatorWithStrictEmailCheck(),
155
-			$this->trustedServers,
156
-			$this->currentUser,
157
-		);
158
-
159
-	}
160
-
161
-	/**
162
-	 * @return ShareAPIController&MockObject
163
-	 */
164
-	private function mockFormatShare() {
165
-		return $this->getMockBuilder(ShareAPIController::class)
166
-			->setConstructorArgs([
167
-				$this->appName,
168
-				$this->request,
169
-				$this->shareManager,
170
-				$this->groupManager,
171
-				$this->userManager,
172
-				$this->rootFolder,
173
-				$this->urlGenerator,
174
-				$this->l,
175
-				$this->config,
176
-				$this->appConfig,
177
-				$this->appManager,
178
-				$this->serverContainer,
179
-				$this->userStatusManager,
180
-				$this->previewManager,
181
-				$this->dateTimeZone,
182
-				$this->logger,
183
-				$this->factory,
184
-				$this->mailer,
185
-				$this->tagManager,
186
-				$this->getEmailValidatorWithStrictEmailCheck(),
187
-				$this->trustedServers,
188
-				$this->currentUser,
189
-			])->onlyMethods(['formatShare'])
190
-			->getMock();
191
-	}
192
-
193
-	private function newShare() {
194
-		return Server::get(IManager::class)->newShare();
195
-	}
196
-
197
-
198
-	private function mockShareAttributes() {
199
-		$formattedShareAttributes = [
200
-			[
201
-				'scope' => 'permissions',
202
-				'key' => 'download',
203
-				'value' => true
204
-			]
205
-		];
206
-
207
-		$shareAttributes = $this->createMock(IShareAttributes::class);
208
-		$shareAttributes->method('toArray')->willReturn($formattedShareAttributes);
209
-		$shareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
210
-
211
-		// send both IShare attributes class and expected json string
212
-		return [$shareAttributes, \json_encode($formattedShareAttributes)];
213
-	}
214
-
215
-	public function testDeleteShareShareNotFound(): void {
216
-		$this->expectException(OCSNotFoundException::class);
217
-		$this->expectExceptionMessage('Wrong share ID, share does not exist');
218
-
219
-		$this->shareManager
220
-			->expects($this->exactly(7))
221
-			->method('getShareById')
222
-			->willReturnCallback(function ($id): void {
223
-				if ($id === 'ocinternal:42' || $id === 'ocRoomShare:42' || $id === 'ocFederatedSharing:42' || $id === 'ocCircleShare:42' || $id === 'ocMailShare:42' || $id === 'deck:42' || $id === 'sciencemesh:42') {
224
-					throw new ShareNotFound();
225
-				} else {
226
-					throw new \Exception();
227
-				}
228
-			});
229
-
230
-		$this->shareManager->method('outgoingServer2ServerSharesAllowed')->willReturn(true);
231
-
232
-		$this->ocs->deleteShare(42);
233
-	}
234
-
235
-	public function testDeleteShare(): void {
236
-		$node = $this->getMockBuilder(File::class)->getMock();
237
-
238
-		$share = $this->newShare();
239
-		$share->setSharedBy($this->currentUser)
240
-			->setNode($node);
241
-		$this->shareManager
242
-			->expects($this->once())
243
-			->method('getShareById')
244
-			->with('ocinternal:42')
245
-			->willReturn($share);
246
-		$this->shareManager
247
-			->expects($this->once())
248
-			->method('deleteShare')
249
-			->with($share);
250
-
251
-		$node->expects($this->once())
252
-			->method('lock')
253
-			->with(ILockingProvider::LOCK_SHARED);
254
-
255
-		$expected = new DataResponse();
256
-		$result = $this->ocs->deleteShare(42);
257
-
258
-		$this->assertInstanceOf(get_class($expected), $result);
259
-		$this->assertEquals($expected->getData(), $result->getData());
260
-	}
261
-
262
-
263
-	public function testDeleteShareLocked(): void {
264
-		$this->expectException(OCSNotFoundException::class);
265
-		$this->expectExceptionMessage('Could not delete share');
266
-
267
-		$node = $this->getMockBuilder(File::class)->getMock();
268
-		$node->method('getId')->willReturn(1);
269
-
270
-		$share = $this->newShare();
271
-		$share->setNode($node);
272
-
273
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
274
-		$this->rootFolder->method('getUserFolder')
275
-			->with($this->currentUser)
276
-			->willReturn($userFolder);
277
-
278
-		$userFolder->method('getById')
279
-			->with($share->getNodeId())
280
-			->willReturn([$node]);
281
-
282
-		$this->shareManager
283
-			->expects($this->once())
284
-			->method('getShareById')
285
-			->with('ocinternal:42')
286
-			->willReturn($share);
287
-
288
-		$this->shareManager
289
-			->expects($this->never())
290
-			->method('deleteShare')
291
-			->with($share);
292
-
293
-		$node->expects($this->once())
294
-			->method('lock')
295
-			->with(ILockingProvider::LOCK_SHARED)
296
-			->willThrowException(new LockedException('mypath'));
297
-
298
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
299
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
300
-
301
-		$this->ocs->deleteShare(42);
302
-	}
303
-
304
-	/**
305
-	 * You can always remove a share that was shared with you
306
-	 */
307
-	public function testDeleteShareWithMe(): void {
308
-		$node = $this->getMockBuilder(File::class)->getMock();
309
-
310
-		$share = $this->newShare();
311
-		$share->setSharedWith($this->currentUser)
312
-			->setShareType(IShare::TYPE_USER)
313
-			->setNode($node);
314
-
315
-		$this->shareManager
316
-			->expects($this->once())
317
-			->method('getShareById')
318
-			->with('ocinternal:42')
319
-			->willReturn($share);
320
-
321
-		$this->shareManager
322
-			->expects($this->once())
323
-			->method('deleteShare')
324
-			->with($share);
325
-
326
-		$node->expects($this->once())
327
-			->method('lock')
328
-			->with(ILockingProvider::LOCK_SHARED);
329
-
330
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
331
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
332
-
333
-		$this->ocs->deleteShare(42);
334
-	}
335
-
336
-	/**
337
-	 * You can always delete a share you own
338
-	 */
339
-	public function testDeleteShareOwner(): void {
340
-		$node = $this->getMockBuilder(File::class)->getMock();
341
-
342
-		$share = $this->newShare();
343
-		$share->setSharedBy($this->currentUser)
344
-			->setNode($node);
345
-
346
-		$this->shareManager
347
-			->expects($this->once())
348
-			->method('getShareById')
349
-			->with('ocinternal:42')
350
-			->willReturn($share);
351
-
352
-		$this->shareManager
353
-			->expects($this->once())
354
-			->method('deleteShare')
355
-			->with($share);
356
-
357
-		$node->expects($this->once())
358
-			->method('lock')
359
-			->with(ILockingProvider::LOCK_SHARED);
360
-
361
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
362
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
363
-
364
-		$this->ocs->deleteShare(42);
365
-	}
366
-
367
-	/**
368
-	 * You can always delete a share when you own
369
-	 * the file path it belong to
370
-	 */
371
-	public function testDeleteShareFileOwner(): void {
372
-		$node = $this->getMockBuilder(File::class)->getMock();
373
-		$node->method('getId')->willReturn(1);
374
-
375
-		$share = $this->newShare();
376
-		$share->setShareOwner($this->currentUser)
377
-			->setNode($node);
378
-
379
-		$this->shareManager
380
-			->expects($this->once())
381
-			->method('getShareById')
382
-			->with('ocinternal:42')
383
-			->willReturn($share);
384
-
385
-		$this->shareManager
386
-			->expects($this->once())
387
-			->method('deleteShare')
388
-			->with($share);
389
-
390
-		$node->expects($this->once())
391
-			->method('lock')
392
-			->with(ILockingProvider::LOCK_SHARED);
393
-
394
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
395
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
396
-
397
-		$this->ocs->deleteShare(42);
398
-	}
399
-
400
-	/**
401
-	 * You can remove (the mountpoint, not the share)
402
-	 * a share if you're in the group the share is shared with
403
-	 */
404
-	public function testDeleteSharedWithMyGroup(): void {
405
-		$node = $this->getMockBuilder(File::class)->getMock();
406
-		$node->method('getId')->willReturn(1);
407
-
408
-		$share = $this->newShare();
409
-		$share->setShareType(IShare::TYPE_GROUP)
410
-			->setSharedWith('group')
411
-			->setNode($node);
412
-
413
-		$this->shareManager
414
-			->expects($this->once())
415
-			->method('getShareById')
416
-			->with('ocinternal:42')
417
-			->willReturn($share);
418
-
419
-		// canDeleteShareFromSelf
420
-		$user = $this->createMock(IUser::class);
421
-		$group = $this->getMockBuilder(IGroup::class)->getMock();
422
-		$this->groupManager
423
-			->method('get')
424
-			->with('group')
425
-			->willReturn($group);
426
-		$this->userManager
427
-			->method('get')
428
-			->with($this->currentUser)
429
-			->willReturn($user);
430
-		$group->method('inGroup')
431
-			->with($user)
432
-			->willReturn(true);
433
-
434
-		$node->expects($this->once())
435
-			->method('lock')
436
-			->with(ILockingProvider::LOCK_SHARED);
437
-
438
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
439
-		$this->rootFolder->method('getUserFolder')
440
-			->with($this->currentUser)
441
-			->willReturn($userFolder);
442
-
443
-		$userFolder->method('getById')
444
-			->with($share->getNodeId())
445
-			->willReturn([$share->getNode()]);
446
-
447
-		$this->shareManager->expects($this->once())
448
-			->method('deleteFromSelf')
449
-			->with($share, $this->currentUser);
450
-
451
-		$this->shareManager->expects($this->never())
452
-			->method('deleteShare');
453
-
454
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShareFromSelf', [$share]));
455
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
456
-
457
-		$this->ocs->deleteShare(42);
458
-	}
459
-
460
-	/**
461
-	 * You cannot remove a share if you're not
462
-	 * in the group the share is shared with
463
-	 */
464
-	public function testDeleteSharedWithGroupIDontBelongTo(): void {
465
-		$this->expectException(OCSNotFoundException::class);
466
-		$this->expectExceptionMessage('Wrong share ID, share does not exist');
467
-
468
-		$node = $this->getMockBuilder(File::class)->getMock();
469
-		$node->method('getId')->willReturn(42);
470
-
471
-		$share = $this->newShare();
472
-		$share->setShareType(IShare::TYPE_GROUP)
473
-			->setSharedWith('group')
474
-			->setNode($node);
475
-
476
-		$this->shareManager
477
-			->expects($this->once())
478
-			->method('getShareById')
479
-			->with('ocinternal:42')
480
-			->willReturn($share);
481
-
482
-		// canDeleteShareFromSelf
483
-		$user = $this->createMock(IUser::class);
484
-		$group = $this->getMockBuilder(IGroup::class)->getMock();
485
-		$this->groupManager
486
-			->method('get')
487
-			->with('group')
488
-			->willReturn($group);
489
-		$this->userManager
490
-			->method('get')
491
-			->with($this->currentUser)
492
-			->willReturn($user);
493
-		$group->method('inGroup')
494
-			->with($user)
495
-			->willReturn(false);
496
-
497
-		$node->expects($this->once())
498
-			->method('lock')
499
-			->with(ILockingProvider::LOCK_SHARED);
500
-
501
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
502
-		$this->rootFolder->method('getUserFolder')
503
-			->with($this->currentUser)
504
-			->willReturn($userFolder);
505
-
506
-		$userFolder->method('getById')
507
-			->with($share->getNodeId())
508
-			->willReturn([$share->getNode()]);
509
-
510
-		$this->shareManager->expects($this->never())
511
-			->method('deleteFromSelf');
512
-
513
-		$this->shareManager->expects($this->never())
514
-			->method('deleteShare');
515
-
516
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShareFromSelf', [$share]));
517
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
518
-
519
-		$this->ocs->deleteShare(42);
520
-	}
521
-
522
-	public function testDeleteShareOwnerless(): void {
523
-		$ocs = $this->mockFormatShare();
524
-
525
-		$mount = $this->createMock(IShareOwnerlessMount::class);
526
-
527
-		$file = $this->createMock(File::class);
528
-		$file
529
-			->expects($this->exactly(2))
530
-			->method('getPermissions')
531
-			->willReturn(Constants::PERMISSION_SHARE);
532
-		$file
533
-			->expects($this->once())
534
-			->method('getMountPoint')
535
-			->willReturn($mount);
536
-
537
-		$userFolder = $this->createMock(Folder::class);
538
-		$userFolder->method('getById')
539
-			->with(2)
540
-			->willReturn([$file]);
541
-		$userFolder->method('getFirstNodeById')
542
-			->with(2)
543
-			->willReturn($file);
544
-
545
-		$this->rootFolder
546
-			->method('getUserFolder')
547
-			->with($this->currentUser)
548
-			->willReturn($userFolder);
549
-
550
-		$share = $this->createMock(IShare::class);
551
-		$share
552
-			->expects($this->once())
553
-			->method('getNode')
554
-			->willReturn($file);
555
-		$share
556
-			->expects($this->exactly(2))
557
-			->method('getNodeId')
558
-			->willReturn(2);
559
-		$share
560
-			->expects($this->exactly(2))
561
-			->method('getPermissions')
562
-			->willReturn(Constants::PERMISSION_SHARE);
563
-
564
-		$this->shareManager
565
-			->expects($this->once())
566
-			->method('getShareById')
567
-			->with('ocinternal:1', $this->currentUser)
568
-			->willReturn($share);
569
-
570
-		$this->shareManager
571
-			->expects($this->once())
572
-			->method('deleteShare')
573
-			->with($share);
574
-
575
-		$result = $ocs->deleteShare(1);
576
-		$this->assertInstanceOf(DataResponse::class, $result);
577
-	}
578
-
579
-	/*
69
+    use EmailValidatorTrait;
70
+
71
+    private string $appName = 'files_sharing';
72
+    private string $currentUser;
73
+
74
+    private ShareAPIController $ocs;
75
+
76
+    private IManager&MockObject $shareManager;
77
+    private IGroupManager&MockObject $groupManager;
78
+    private IUserManager&MockObject $userManager;
79
+    private IRequest&MockObject $request;
80
+    private IRootFolder&MockObject $rootFolder;
81
+    private IURLGenerator&MockObject $urlGenerator;
82
+    private IL10N&MockObject $l;
83
+    private IConfig&MockObject $config;
84
+    private IAppConfig&MockObject $appConfig;
85
+    private IAppManager&MockObject $appManager;
86
+    private ContainerInterface&MockObject $serverContainer;
87
+    private IUserStatusManager&MockObject $userStatusManager;
88
+    private IPreview&MockObject $previewManager;
89
+    private IDateTimeZone&MockObject $dateTimeZone;
90
+    private LoggerInterface&MockObject $logger;
91
+    private IProviderFactory&MockObject $factory;
92
+    private IMailer&MockObject $mailer;
93
+    private ITagManager&MockObject $tagManager;
94
+    private TrustedServers&MockObject $trustedServers;
95
+
96
+    protected function setUp(): void {
97
+        $this->shareManager = $this->createMock(IManager::class);
98
+        $this->shareManager
99
+            ->expects($this->any())
100
+            ->method('shareApiEnabled')
101
+            ->willReturn(true);
102
+        $this->shareManager
103
+            ->expects($this->any())
104
+            ->method('shareProviderExists')->willReturn(true);
105
+        $this->groupManager = $this->createMock(IGroupManager::class);
106
+        $this->userManager = $this->createMock(IUserManager::class);
107
+        $this->request = $this->createMock(IRequest::class);
108
+        $this->rootFolder = $this->createMock(IRootFolder::class);
109
+        $this->urlGenerator = $this->createMock(IURLGenerator::class);
110
+        $this->currentUser = 'currentUser';
111
+
112
+        $this->l = $this->createMock(IL10N::class);
113
+        $this->l->method('t')
114
+            ->willReturnCallback(function ($text, $parameters = []) {
115
+                return vsprintf($text, $parameters);
116
+            });
117
+        $this->config = $this->createMock(IConfig::class);
118
+        $this->appConfig = $this->createMock(IAppConfig::class);
119
+        $this->appManager = $this->createMock(IAppManager::class);
120
+        $this->serverContainer = $this->createMock(ContainerInterface::class);
121
+        $this->userStatusManager = $this->createMock(IUserStatusManager::class);
122
+        $this->previewManager = $this->createMock(IPreview::class);
123
+        $this->previewManager->method('isAvailable')
124
+            ->willReturnCallback(function ($fileInfo) {
125
+                return $fileInfo->getMimeType() === 'mimeWithPreview';
126
+            });
127
+        $this->dateTimeZone = $this->createMock(IDateTimeZone::class);
128
+        $this->logger = $this->createMock(LoggerInterface::class);
129
+        $this->factory = $this->createMock(IProviderFactory::class);
130
+        $this->mailer = $this->createMock(IMailer::class);
131
+        $this->tagManager = $this->createMock(ITagManager::class);
132
+        $this->trustedServers = $this->createMock(TrustedServers::class);
133
+
134
+        $this->ocs = new ShareAPIController(
135
+            $this->appName,
136
+            $this->request,
137
+            $this->shareManager,
138
+            $this->groupManager,
139
+            $this->userManager,
140
+            $this->rootFolder,
141
+            $this->urlGenerator,
142
+            $this->l,
143
+            $this->config,
144
+            $this->appConfig,
145
+            $this->appManager,
146
+            $this->serverContainer,
147
+            $this->userStatusManager,
148
+            $this->previewManager,
149
+            $this->dateTimeZone,
150
+            $this->logger,
151
+            $this->factory,
152
+            $this->mailer,
153
+            $this->tagManager,
154
+            $this->getEmailValidatorWithStrictEmailCheck(),
155
+            $this->trustedServers,
156
+            $this->currentUser,
157
+        );
158
+
159
+    }
160
+
161
+    /**
162
+     * @return ShareAPIController&MockObject
163
+     */
164
+    private function mockFormatShare() {
165
+        return $this->getMockBuilder(ShareAPIController::class)
166
+            ->setConstructorArgs([
167
+                $this->appName,
168
+                $this->request,
169
+                $this->shareManager,
170
+                $this->groupManager,
171
+                $this->userManager,
172
+                $this->rootFolder,
173
+                $this->urlGenerator,
174
+                $this->l,
175
+                $this->config,
176
+                $this->appConfig,
177
+                $this->appManager,
178
+                $this->serverContainer,
179
+                $this->userStatusManager,
180
+                $this->previewManager,
181
+                $this->dateTimeZone,
182
+                $this->logger,
183
+                $this->factory,
184
+                $this->mailer,
185
+                $this->tagManager,
186
+                $this->getEmailValidatorWithStrictEmailCheck(),
187
+                $this->trustedServers,
188
+                $this->currentUser,
189
+            ])->onlyMethods(['formatShare'])
190
+            ->getMock();
191
+    }
192
+
193
+    private function newShare() {
194
+        return Server::get(IManager::class)->newShare();
195
+    }
196
+
197
+
198
+    private function mockShareAttributes() {
199
+        $formattedShareAttributes = [
200
+            [
201
+                'scope' => 'permissions',
202
+                'key' => 'download',
203
+                'value' => true
204
+            ]
205
+        ];
206
+
207
+        $shareAttributes = $this->createMock(IShareAttributes::class);
208
+        $shareAttributes->method('toArray')->willReturn($formattedShareAttributes);
209
+        $shareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
210
+
211
+        // send both IShare attributes class and expected json string
212
+        return [$shareAttributes, \json_encode($formattedShareAttributes)];
213
+    }
214
+
215
+    public function testDeleteShareShareNotFound(): void {
216
+        $this->expectException(OCSNotFoundException::class);
217
+        $this->expectExceptionMessage('Wrong share ID, share does not exist');
218
+
219
+        $this->shareManager
220
+            ->expects($this->exactly(7))
221
+            ->method('getShareById')
222
+            ->willReturnCallback(function ($id): void {
223
+                if ($id === 'ocinternal:42' || $id === 'ocRoomShare:42' || $id === 'ocFederatedSharing:42' || $id === 'ocCircleShare:42' || $id === 'ocMailShare:42' || $id === 'deck:42' || $id === 'sciencemesh:42') {
224
+                    throw new ShareNotFound();
225
+                } else {
226
+                    throw new \Exception();
227
+                }
228
+            });
229
+
230
+        $this->shareManager->method('outgoingServer2ServerSharesAllowed')->willReturn(true);
231
+
232
+        $this->ocs->deleteShare(42);
233
+    }
234
+
235
+    public function testDeleteShare(): void {
236
+        $node = $this->getMockBuilder(File::class)->getMock();
237
+
238
+        $share = $this->newShare();
239
+        $share->setSharedBy($this->currentUser)
240
+            ->setNode($node);
241
+        $this->shareManager
242
+            ->expects($this->once())
243
+            ->method('getShareById')
244
+            ->with('ocinternal:42')
245
+            ->willReturn($share);
246
+        $this->shareManager
247
+            ->expects($this->once())
248
+            ->method('deleteShare')
249
+            ->with($share);
250
+
251
+        $node->expects($this->once())
252
+            ->method('lock')
253
+            ->with(ILockingProvider::LOCK_SHARED);
254
+
255
+        $expected = new DataResponse();
256
+        $result = $this->ocs->deleteShare(42);
257
+
258
+        $this->assertInstanceOf(get_class($expected), $result);
259
+        $this->assertEquals($expected->getData(), $result->getData());
260
+    }
261
+
262
+
263
+    public function testDeleteShareLocked(): void {
264
+        $this->expectException(OCSNotFoundException::class);
265
+        $this->expectExceptionMessage('Could not delete share');
266
+
267
+        $node = $this->getMockBuilder(File::class)->getMock();
268
+        $node->method('getId')->willReturn(1);
269
+
270
+        $share = $this->newShare();
271
+        $share->setNode($node);
272
+
273
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
274
+        $this->rootFolder->method('getUserFolder')
275
+            ->with($this->currentUser)
276
+            ->willReturn($userFolder);
277
+
278
+        $userFolder->method('getById')
279
+            ->with($share->getNodeId())
280
+            ->willReturn([$node]);
281
+
282
+        $this->shareManager
283
+            ->expects($this->once())
284
+            ->method('getShareById')
285
+            ->with('ocinternal:42')
286
+            ->willReturn($share);
287
+
288
+        $this->shareManager
289
+            ->expects($this->never())
290
+            ->method('deleteShare')
291
+            ->with($share);
292
+
293
+        $node->expects($this->once())
294
+            ->method('lock')
295
+            ->with(ILockingProvider::LOCK_SHARED)
296
+            ->willThrowException(new LockedException('mypath'));
297
+
298
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
299
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
300
+
301
+        $this->ocs->deleteShare(42);
302
+    }
303
+
304
+    /**
305
+     * You can always remove a share that was shared with you
306
+     */
307
+    public function testDeleteShareWithMe(): void {
308
+        $node = $this->getMockBuilder(File::class)->getMock();
309
+
310
+        $share = $this->newShare();
311
+        $share->setSharedWith($this->currentUser)
312
+            ->setShareType(IShare::TYPE_USER)
313
+            ->setNode($node);
314
+
315
+        $this->shareManager
316
+            ->expects($this->once())
317
+            ->method('getShareById')
318
+            ->with('ocinternal:42')
319
+            ->willReturn($share);
320
+
321
+        $this->shareManager
322
+            ->expects($this->once())
323
+            ->method('deleteShare')
324
+            ->with($share);
325
+
326
+        $node->expects($this->once())
327
+            ->method('lock')
328
+            ->with(ILockingProvider::LOCK_SHARED);
329
+
330
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
331
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
332
+
333
+        $this->ocs->deleteShare(42);
334
+    }
335
+
336
+    /**
337
+     * You can always delete a share you own
338
+     */
339
+    public function testDeleteShareOwner(): void {
340
+        $node = $this->getMockBuilder(File::class)->getMock();
341
+
342
+        $share = $this->newShare();
343
+        $share->setSharedBy($this->currentUser)
344
+            ->setNode($node);
345
+
346
+        $this->shareManager
347
+            ->expects($this->once())
348
+            ->method('getShareById')
349
+            ->with('ocinternal:42')
350
+            ->willReturn($share);
351
+
352
+        $this->shareManager
353
+            ->expects($this->once())
354
+            ->method('deleteShare')
355
+            ->with($share);
356
+
357
+        $node->expects($this->once())
358
+            ->method('lock')
359
+            ->with(ILockingProvider::LOCK_SHARED);
360
+
361
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
362
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
363
+
364
+        $this->ocs->deleteShare(42);
365
+    }
366
+
367
+    /**
368
+     * You can always delete a share when you own
369
+     * the file path it belong to
370
+     */
371
+    public function testDeleteShareFileOwner(): void {
372
+        $node = $this->getMockBuilder(File::class)->getMock();
373
+        $node->method('getId')->willReturn(1);
374
+
375
+        $share = $this->newShare();
376
+        $share->setShareOwner($this->currentUser)
377
+            ->setNode($node);
378
+
379
+        $this->shareManager
380
+            ->expects($this->once())
381
+            ->method('getShareById')
382
+            ->with('ocinternal:42')
383
+            ->willReturn($share);
384
+
385
+        $this->shareManager
386
+            ->expects($this->once())
387
+            ->method('deleteShare')
388
+            ->with($share);
389
+
390
+        $node->expects($this->once())
391
+            ->method('lock')
392
+            ->with(ILockingProvider::LOCK_SHARED);
393
+
394
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
395
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
396
+
397
+        $this->ocs->deleteShare(42);
398
+    }
399
+
400
+    /**
401
+     * You can remove (the mountpoint, not the share)
402
+     * a share if you're in the group the share is shared with
403
+     */
404
+    public function testDeleteSharedWithMyGroup(): void {
405
+        $node = $this->getMockBuilder(File::class)->getMock();
406
+        $node->method('getId')->willReturn(1);
407
+
408
+        $share = $this->newShare();
409
+        $share->setShareType(IShare::TYPE_GROUP)
410
+            ->setSharedWith('group')
411
+            ->setNode($node);
412
+
413
+        $this->shareManager
414
+            ->expects($this->once())
415
+            ->method('getShareById')
416
+            ->with('ocinternal:42')
417
+            ->willReturn($share);
418
+
419
+        // canDeleteShareFromSelf
420
+        $user = $this->createMock(IUser::class);
421
+        $group = $this->getMockBuilder(IGroup::class)->getMock();
422
+        $this->groupManager
423
+            ->method('get')
424
+            ->with('group')
425
+            ->willReturn($group);
426
+        $this->userManager
427
+            ->method('get')
428
+            ->with($this->currentUser)
429
+            ->willReturn($user);
430
+        $group->method('inGroup')
431
+            ->with($user)
432
+            ->willReturn(true);
433
+
434
+        $node->expects($this->once())
435
+            ->method('lock')
436
+            ->with(ILockingProvider::LOCK_SHARED);
437
+
438
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
439
+        $this->rootFolder->method('getUserFolder')
440
+            ->with($this->currentUser)
441
+            ->willReturn($userFolder);
442
+
443
+        $userFolder->method('getById')
444
+            ->with($share->getNodeId())
445
+            ->willReturn([$share->getNode()]);
446
+
447
+        $this->shareManager->expects($this->once())
448
+            ->method('deleteFromSelf')
449
+            ->with($share, $this->currentUser);
450
+
451
+        $this->shareManager->expects($this->never())
452
+            ->method('deleteShare');
453
+
454
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShareFromSelf', [$share]));
455
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
456
+
457
+        $this->ocs->deleteShare(42);
458
+    }
459
+
460
+    /**
461
+     * You cannot remove a share if you're not
462
+     * in the group the share is shared with
463
+     */
464
+    public function testDeleteSharedWithGroupIDontBelongTo(): void {
465
+        $this->expectException(OCSNotFoundException::class);
466
+        $this->expectExceptionMessage('Wrong share ID, share does not exist');
467
+
468
+        $node = $this->getMockBuilder(File::class)->getMock();
469
+        $node->method('getId')->willReturn(42);
470
+
471
+        $share = $this->newShare();
472
+        $share->setShareType(IShare::TYPE_GROUP)
473
+            ->setSharedWith('group')
474
+            ->setNode($node);
475
+
476
+        $this->shareManager
477
+            ->expects($this->once())
478
+            ->method('getShareById')
479
+            ->with('ocinternal:42')
480
+            ->willReturn($share);
481
+
482
+        // canDeleteShareFromSelf
483
+        $user = $this->createMock(IUser::class);
484
+        $group = $this->getMockBuilder(IGroup::class)->getMock();
485
+        $this->groupManager
486
+            ->method('get')
487
+            ->with('group')
488
+            ->willReturn($group);
489
+        $this->userManager
490
+            ->method('get')
491
+            ->with($this->currentUser)
492
+            ->willReturn($user);
493
+        $group->method('inGroup')
494
+            ->with($user)
495
+            ->willReturn(false);
496
+
497
+        $node->expects($this->once())
498
+            ->method('lock')
499
+            ->with(ILockingProvider::LOCK_SHARED);
500
+
501
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
502
+        $this->rootFolder->method('getUserFolder')
503
+            ->with($this->currentUser)
504
+            ->willReturn($userFolder);
505
+
506
+        $userFolder->method('getById')
507
+            ->with($share->getNodeId())
508
+            ->willReturn([$share->getNode()]);
509
+
510
+        $this->shareManager->expects($this->never())
511
+            ->method('deleteFromSelf');
512
+
513
+        $this->shareManager->expects($this->never())
514
+            ->method('deleteShare');
515
+
516
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShareFromSelf', [$share]));
517
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
518
+
519
+        $this->ocs->deleteShare(42);
520
+    }
521
+
522
+    public function testDeleteShareOwnerless(): void {
523
+        $ocs = $this->mockFormatShare();
524
+
525
+        $mount = $this->createMock(IShareOwnerlessMount::class);
526
+
527
+        $file = $this->createMock(File::class);
528
+        $file
529
+            ->expects($this->exactly(2))
530
+            ->method('getPermissions')
531
+            ->willReturn(Constants::PERMISSION_SHARE);
532
+        $file
533
+            ->expects($this->once())
534
+            ->method('getMountPoint')
535
+            ->willReturn($mount);
536
+
537
+        $userFolder = $this->createMock(Folder::class);
538
+        $userFolder->method('getById')
539
+            ->with(2)
540
+            ->willReturn([$file]);
541
+        $userFolder->method('getFirstNodeById')
542
+            ->with(2)
543
+            ->willReturn($file);
544
+
545
+        $this->rootFolder
546
+            ->method('getUserFolder')
547
+            ->with($this->currentUser)
548
+            ->willReturn($userFolder);
549
+
550
+        $share = $this->createMock(IShare::class);
551
+        $share
552
+            ->expects($this->once())
553
+            ->method('getNode')
554
+            ->willReturn($file);
555
+        $share
556
+            ->expects($this->exactly(2))
557
+            ->method('getNodeId')
558
+            ->willReturn(2);
559
+        $share
560
+            ->expects($this->exactly(2))
561
+            ->method('getPermissions')
562
+            ->willReturn(Constants::PERMISSION_SHARE);
563
+
564
+        $this->shareManager
565
+            ->expects($this->once())
566
+            ->method('getShareById')
567
+            ->with('ocinternal:1', $this->currentUser)
568
+            ->willReturn($share);
569
+
570
+        $this->shareManager
571
+            ->expects($this->once())
572
+            ->method('deleteShare')
573
+            ->with($share);
574
+
575
+        $result = $ocs->deleteShare(1);
576
+        $this->assertInstanceOf(DataResponse::class, $result);
577
+    }
578
+
579
+    /*
580 580
 	 * FIXME: Enable once we have a federated Share Provider
581 581
 
582 582
 	public function testGetGetShareNotExists() {
@@ -591,5061 +591,5061 @@  discard block
 block discarded – undo
591 591
 	}
592 592
 	*/
593 593
 
594
-	public function createShare(
595
-		int $id,
596
-		int $shareType,
597
-		?string $sharedWith,
598
-		string $sharedBy,
599
-		string $shareOwner,
600
-		File|Folder|null $node,
601
-		int $permissions,
602
-		int $shareTime,
603
-		?\DateTime $expiration,
604
-		int $parent,
605
-		string $target,
606
-		int $mail_send,
607
-		string $note = '',
608
-		?string $token = null,
609
-		?string $password = null,
610
-		string $label = '',
611
-		?IShareAttributes $attributes = null,
612
-	): MockObject {
613
-		$share = $this->createMock(IShare::class);
614
-		$share->method('getId')->willReturn($id);
615
-		$share->method('getShareType')->willReturn($shareType);
616
-		$share->method('getSharedWith')->willReturn($sharedWith);
617
-		$share->method('getSharedBy')->willReturn($sharedBy);
618
-		$share->method('getShareOwner')->willReturn($shareOwner);
619
-		$share->method('getNode')->willReturn($node);
620
-		$share->method('getPermissions')->willReturn($permissions);
621
-		$share->method('getNote')->willReturn($note);
622
-		$share->method('getLabel')->willReturn($label);
623
-		$share->method('getAttributes')->willReturn($attributes);
624
-		$time = new \DateTime();
625
-		$time->setTimestamp($shareTime);
626
-		$share->method('getShareTime')->willReturn($time);
627
-		$share->method('getExpirationDate')->willReturn($expiration);
628
-		$share->method('getTarget')->willReturn($target);
629
-		$share->method('getMailSend')->willReturn($mail_send);
630
-		$share->method('getToken')->willReturn($token);
631
-		$share->method('getPassword')->willReturn($password);
632
-
633
-		if ($shareType === IShare::TYPE_USER
634
-			|| $shareType === IShare::TYPE_GROUP
635
-			|| $shareType === IShare::TYPE_LINK) {
636
-			$share->method('getFullId')->willReturn('ocinternal:' . $id);
637
-		}
638
-
639
-		return $share;
640
-	}
641
-
642
-	public static function dataGetShare(): array {
643
-		$data = [];
644
-
645
-		$file = [
646
-			'class' => File::class,
647
-			'id' => 1,
648
-			'path' => 'file',
649
-			'mimeType' => 'myMimeType',
650
-		];
651
-
652
-		$folder = [
653
-			'class' => Folder::class,
654
-			'id' => 2,
655
-			'path' => 'folder',
656
-			'mimeType' => 'myFolderMimeType',
657
-		];
658
-
659
-		// File shared with user
660
-		$share = [
661
-			100,
662
-			IShare::TYPE_USER,
663
-			'userId',
664
-			'initiatorId',
665
-			'ownerId',
666
-			$file,
667
-			4,
668
-			5,
669
-			null,
670
-			6,
671
-			'target',
672
-			0,
673
-			'personal note',
674
-			null,
675
-			null,
676
-			'',
677
-			[],
678
-		];
679
-		$expected = [
680
-			'id' => 100,
681
-			'share_type' => IShare::TYPE_USER,
682
-			'share_with' => 'userId',
683
-			'share_with_displayname' => 'userDisplay',
684
-			'share_with_displayname_unique' => '[email protected]',
685
-			'uid_owner' => 'initiatorId',
686
-			'displayname_owner' => 'initiatorDisplay',
687
-			'item_type' => 'file',
688
-			'item_source' => 1,
689
-			'file_source' => 1,
690
-			'file_target' => 'target',
691
-			'file_parent' => 3,
692
-			'token' => null,
693
-			'expiration' => null,
694
-			'permissions' => 4,
695
-			'stime' => 5,
696
-			'parent' => null,
697
-			'storage_id' => 'STORAGE',
698
-			'path' => 'file',
699
-			'storage' => 101,
700
-			'mail_send' => 0,
701
-			'uid_file_owner' => 'ownerId',
702
-			'note' => 'personal note',
703
-			'label' => '',
704
-			'displayname_file_owner' => 'ownerDisplay',
705
-			'mimetype' => 'myMimeType',
706
-			'has_preview' => false,
707
-			'hide_download' => 0,
708
-			'can_edit' => false,
709
-			'can_delete' => false,
710
-			'item_size' => 123465,
711
-			'item_mtime' => 1234567890,
712
-			'item_permissions' => 4,
713
-			'is-mount-root' => false,
714
-			'mount-type' => '',
715
-		];
716
-		$data['File shared with user'] = [$share, $expected, true];
717
-
718
-		// Folder shared with group
719
-		$share = [
720
-			101,
721
-			IShare::TYPE_GROUP,
722
-			'groupId',
723
-			'initiatorId',
724
-			'ownerId',
725
-			$folder,
726
-			4,
727
-			5,
728
-			null,
729
-			6,
730
-			'target',
731
-			0,
732
-			'personal note',
733
-			null,
734
-			null,
735
-			'',
736
-			[],
737
-		];
738
-		$expected = [
739
-			'id' => 101,
740
-			'share_type' => IShare::TYPE_GROUP,
741
-			'share_with' => 'groupId',
742
-			'share_with_displayname' => 'groupId',
743
-			'uid_owner' => 'initiatorId',
744
-			'displayname_owner' => 'initiatorDisplay',
745
-			'item_type' => 'folder',
746
-			'item_source' => 2,
747
-			'file_source' => 2,
748
-			'file_target' => 'target',
749
-			'file_parent' => 3,
750
-			'token' => null,
751
-			'expiration' => null,
752
-			'permissions' => 4,
753
-			'stime' => 5,
754
-			'parent' => null,
755
-			'storage_id' => 'STORAGE',
756
-			'path' => 'folder',
757
-			'storage' => 101,
758
-			'mail_send' => 0,
759
-			'uid_file_owner' => 'ownerId',
760
-			'note' => 'personal note',
761
-			'label' => '',
762
-			'displayname_file_owner' => 'ownerDisplay',
763
-			'mimetype' => 'myFolderMimeType',
764
-			'has_preview' => false,
765
-			'hide_download' => 0,
766
-			'can_edit' => false,
767
-			'can_delete' => false,
768
-			'item_size' => 123465,
769
-			'item_mtime' => 1234567890,
770
-			'item_permissions' => 4,
771
-			'is-mount-root' => false,
772
-			'mount-type' => '',
773
-		];
774
-		$data['Folder shared with group'] = [$share, $expected, true];
775
-
776
-		// File shared by link with Expire
777
-		$expire = \DateTime::createFromFormat('Y-m-d h:i:s', '2000-01-02 01:02:03');
778
-		$share = [
779
-			101,
780
-			IShare::TYPE_LINK,
781
-			null,
782
-			'initiatorId',
783
-			'ownerId',
784
-			$folder,
785
-			4,
786
-			5,
787
-			$expire,
788
-			6,
789
-			'target',
790
-			0,
791
-			'personal note',
792
-			'token',
793
-			'password',
794
-			'first link share'
795
-		];
796
-		$expected = [
797
-			'id' => 101,
798
-			'share_type' => IShare::TYPE_LINK,
799
-			'password' => 'password',
800
-			'share_with' => 'password',
801
-			'share_with_displayname' => '(Shared link)',
802
-			'send_password_by_talk' => false,
803
-			'uid_owner' => 'initiatorId',
804
-			'displayname_owner' => 'initiatorDisplay',
805
-			'item_type' => 'folder',
806
-			'item_source' => 2,
807
-			'file_source' => 2,
808
-			'file_target' => 'target',
809
-			'file_parent' => 3,
810
-			'token' => 'token',
811
-			'expiration' => '2000-01-02 00:00:00',
812
-			'permissions' => 4,
813
-			'attributes' => null,
814
-			'stime' => 5,
815
-			'parent' => null,
816
-			'storage_id' => 'STORAGE',
817
-			'path' => 'folder',
818
-			'storage' => 101,
819
-			'mail_send' => 0,
820
-			'url' => 'url',
821
-			'uid_file_owner' => 'ownerId',
822
-			'note' => 'personal note',
823
-			'label' => 'first link share',
824
-			'displayname_file_owner' => 'ownerDisplay',
825
-			'mimetype' => 'myFolderMimeType',
826
-			'has_preview' => false,
827
-			'hide_download' => 0,
828
-			'can_edit' => false,
829
-			'can_delete' => false,
830
-			'item_size' => 123465,
831
-			'item_mtime' => 1234567890,
832
-			'item_permissions' => 4,
833
-			'is-mount-root' => false,
834
-			'mount-type' => '',
835
-		];
836
-		$data['File shared by link with Expire'] = [$share, $expected, false];
837
-
838
-		return $data;
839
-	}
840
-
841
-	#[DataProvider('dataGetShare')]
842
-	public function testGetShare(array $shareParams, array $result, bool $attributes): void {
843
-
844
-		$cache = $this->createMock(ICache::class);
845
-		$cache->method('getNumericStorageId')->willReturn(101);
846
-
847
-		$storage = $this->createMock(IStorage::class);
848
-		$storage->method('getId')->willReturn('STORAGE');
849
-		$storage->method('getCache')->willReturn($cache);
850
-
851
-		$parentFolder = $this->createMock(Folder::class);
852
-		$parentFolder->method('getId')->willReturn(3);
853
-
854
-		$mountPoint = $this->createMock(IMountPoint::class);
855
-		$mountPoint->method('getMountType')->willReturn('');
856
-
857
-		$nodeParams = $shareParams[5];
858
-		$node = $this->createMock($nodeParams['class']);
859
-		$node->method('getId')->willReturn($nodeParams['id']);
860
-		$node->method('getPath')->willReturn($nodeParams['path']);
861
-		$node->method('getStorage')->willReturn($storage);
862
-		$node->method('getParent')->willReturn($parentFolder);
863
-		$node->method('getSize')->willReturn(123465);
864
-		$node->method('getMTime')->willReturn(1234567890);
865
-		$node->method('getMimeType')->willReturn($nodeParams['mimeType']);
866
-		$node->method('getMountPoint')->willReturn($mountPoint);
867
-
868
-		$shareParams[5] = $node;
869
-
870
-		if ($attributes) {
871
-			[$shareAttributes, $shareAttributesReturnJson] = $this->mockShareAttributes();
872
-			$result['attributes'] = $shareAttributesReturnJson;
873
-			$shareParams[16] = $shareAttributes;
874
-		}
875
-
876
-		$share = $this->createShare(...$shareParams);
877
-		/** @var ShareAPIController&MockObject $ocs */
878
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
879
-			->setConstructorArgs([
880
-				$this->appName,
881
-				$this->request,
882
-				$this->shareManager,
883
-				$this->groupManager,
884
-				$this->userManager,
885
-				$this->rootFolder,
886
-				$this->urlGenerator,
887
-				$this->l,
888
-				$this->config,
889
-				$this->appConfig,
890
-				$this->appManager,
891
-				$this->serverContainer,
892
-				$this->userStatusManager,
893
-				$this->previewManager,
894
-				$this->dateTimeZone,
895
-				$this->logger,
896
-				$this->factory,
897
-				$this->mailer,
898
-				$this->tagManager,
899
-				$this->getEmailValidatorWithStrictEmailCheck(),
900
-				$this->trustedServers,
901
-				$this->currentUser,
902
-			])
903
-			->onlyMethods(['canAccessShare'])
904
-			->getMock();
905
-
906
-		$ocs->expects($this->any())
907
-			->method('canAccessShare')
908
-			->willReturn(true);
909
-
910
-		$this->shareManager
911
-			->expects($this->any())
912
-			->method('getShareById')
913
-			->with($share->getFullId(), 'currentUser')
914
-			->willReturn($share);
915
-
916
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
917
-		$userFolder
918
-			->method('getRelativePath')
919
-			->willReturnArgument(0);
920
-
921
-		$userFolder->method('getById')
922
-			->with($share->getNodeId())
923
-			->willReturn([$share->getNode()]);
924
-		$userFolder->method('getFirstNodeById')
925
-			->with($share->getNodeId())
926
-			->willReturn($share->getNode());
927
-
928
-		$this->rootFolder->method('getUserFolder')
929
-			->with($this->currentUser)
930
-			->willReturn($userFolder);
931
-
932
-		$this->urlGenerator
933
-			->method('linkToRouteAbsolute')
934
-			->willReturn('url');
935
-
936
-		$initiator = $this->getMockBuilder(IUser::class)->getMock();
937
-		$initiator->method('getUID')->willReturn('initiatorId');
938
-		$initiator->method('getDisplayName')->willReturn('initiatorDisplay');
939
-
940
-		$owner = $this->getMockBuilder(IUser::class)->getMock();
941
-		$owner->method('getUID')->willReturn('ownerId');
942
-		$owner->method('getDisplayName')->willReturn('ownerDisplay');
943
-
944
-		$user = $this->getMockBuilder(IUser::class)->getMock();
945
-		$user->method('getUID')->willReturn('userId');
946
-		$user->method('getDisplayName')->willReturn('userDisplay');
947
-		$user->method('getSystemEMailAddress')->willReturn('[email protected]');
948
-
949
-		$group = $this->getMockBuilder(IGroup::class)->getMock();
950
-		$group->method('getGID')->willReturn('groupId');
951
-
952
-		$this->userManager->method('get')->willReturnMap([
953
-			['userId', $user],
954
-			['initiatorId', $initiator],
955
-			['ownerId', $owner],
956
-		]);
957
-		$this->groupManager->method('get')->willReturnMap([
958
-			['group', $group],
959
-		]);
960
-		$this->dateTimeZone->method('getTimezone')->willReturn(new \DateTimeZone('UTC'));
961
-
962
-		$data = $ocs->getShare((string)$share->getId())->getData()[0];
963
-		$this->assertEquals($result, $data);
964
-	}
965
-
966
-
967
-	public function testGetShareInvalidNode(): void {
968
-		$this->expectException(OCSNotFoundException::class);
969
-		$this->expectExceptionMessage('Wrong share ID, share does not exist');
970
-
971
-		$share = Server::get(IManager::class)->newShare();
972
-		$share->setSharedBy('initiator')
973
-			->setSharedWith('recipient')
974
-			->setShareOwner('owner');
975
-
976
-		$this->shareManager
977
-			->expects($this->once())
978
-			->method('getShareById')
979
-			->with('ocinternal:42', 'currentUser')
980
-			->willReturn($share);
981
-
982
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
983
-		$this->rootFolder->method('getUserFolder')
984
-			->with($this->currentUser)
985
-			->willReturn($userFolder);
986
-
987
-		$this->ocs->getShare('42');
988
-	}
989
-
990
-	public static function dataGetShares(): array {
991
-		$file1 = [
992
-			'class' => File::class,
993
-			'methods' => [
994
-				'getName' => 'file1',
995
-			]
996
-		];
997
-		$file2 = [
998
-			'class' => File::class,
999
-			'methods' => [
1000
-				'getName' => 'file2',
1001
-			]
1002
-		];
1003
-
1004
-		$folder = [
1005
-			'class' => Folder::class,
1006
-			'methods' => [
1007
-				'getDirectoryListing' => [$file1, $file2]
1008
-			]
1009
-		];
1010
-
1011
-		$file1UserShareOwner = [
1012
-			'type' => IShare::TYPE_USER,
1013
-			'sharedWith' => 'recipient',
1014
-			'sharedBy' => 'initiator',
1015
-			'owner' => 'currentUser',
1016
-			'node' => $file1,
1017
-			'id' => 4,
1018
-		];
1019
-
1020
-		$file1UserShareOwnerExpected = [
1021
-			'id' => 4,
1022
-			'share_type' => IShare::TYPE_USER,
1023
-		];
1024
-
1025
-		$file1UserShareInitiator = [
1026
-			'type' => IShare::TYPE_USER,
1027
-			'sharedWith' => 'recipient',
1028
-			'sharedBy' => 'currentUser',
1029
-			'owner' => 'owner',
1030
-			'node' => $file1,
1031
-			'id' => 8,
1032
-		];
1033
-
1034
-		$file1UserShareInitiatorExpected = [
1035
-			'id' => 8,
1036
-			'share_type' => IShare::TYPE_USER,
1037
-		];
1038
-
1039
-		$file1UserShareRecipient = [
1040
-			'type' => IShare::TYPE_USER,
1041
-			'sharedWith' => 'currentUser',
1042
-			'sharedBy' => 'initiator',
1043
-			'owner' => 'owner',
1044
-			'node' => $file1,
1045
-			'id' => 15,
1046
-		];
1047
-
1048
-		$file1UserShareRecipientExpected = [
1049
-			'id' => 15,
1050
-			'share_type' => IShare::TYPE_USER,
1051
-		];
1052
-
1053
-		$file1UserShareOther = [
1054
-			'type' => IShare::TYPE_USER,
1055
-			'sharedWith' => 'recipient',
1056
-			'sharedBy' => 'initiator',
1057
-			'owner' => 'owner',
1058
-			'node' => $file1,
1059
-			'id' => 16,
1060
-		];
1061
-
1062
-		$file1UserShareOtherExpected = [
1063
-			'id' => 16,
1064
-			'share_type' => IShare::TYPE_USER,
1065
-		];
1066
-
1067
-		$file1GroupShareOwner = [
1068
-			'type' => IShare::TYPE_GROUP,
1069
-			'sharedWith' => 'recipient',
1070
-			'sharedBy' => 'initiator',
1071
-			'owner' => 'currentUser',
1072
-			'node' => $file1,
1073
-			'id' => 23,
1074
-		];
1075
-
1076
-		$file1GroupShareOwnerExpected = [
1077
-			'id' => 23,
1078
-			'share_type' => IShare::TYPE_GROUP,
1079
-		];
1080
-
1081
-		$file1GroupShareRecipient = [
1082
-			'type' => IShare::TYPE_GROUP,
1083
-			'sharedWith' => 'currentUserGroup',
1084
-			'sharedBy' => 'initiator',
1085
-			'owner' => 'owner',
1086
-			'node' => $file1,
1087
-			'id' => 42,
1088
-		];
1089
-
1090
-		$file1GroupShareRecipientExpected = [
1091
-			'id' => 42,
1092
-			'share_type' => IShare::TYPE_GROUP,
1093
-		];
1094
-
1095
-		$file1GroupShareOther = [
1096
-			'type' => IShare::TYPE_GROUP,
1097
-			'sharedWith' => 'recipient',
1098
-			'sharedBy' => 'initiator',
1099
-			'owner' => 'owner',
1100
-			'node' => $file1,
1101
-			'id' => 108,
1102
-		];
1103
-
1104
-		$file1LinkShareOwner = [
1105
-			'type' => IShare::TYPE_LINK,
1106
-			'sharedWith' => 'recipient',
1107
-			'sharedBy' => 'initiator',
1108
-			'owner' => 'currentUser',
1109
-			'node' => $file1,
1110
-			'id' => 415,
1111
-		];
1112
-
1113
-		$file1LinkShareOwnerExpected = [
1114
-			'id' => 415,
1115
-			'share_type' => IShare::TYPE_LINK,
1116
-		];
1117
-
1118
-		$file1EmailShareOwner = [
1119
-			'type' => IShare::TYPE_EMAIL,
1120
-			'sharedWith' => 'recipient',
1121
-			'sharedBy' => 'initiator',
1122
-			'owner' => 'currentUser',
1123
-			'node' => $file1,
1124
-			'id' => 416,
1125
-		];
1126
-
1127
-		$file1EmailShareOwnerExpected = [
1128
-			'id' => 416,
1129
-			'share_type' => IShare::TYPE_EMAIL,
1130
-		];
1131
-
1132
-		$file1CircleShareOwner = [
1133
-			'type' => IShare::TYPE_CIRCLE,
1134
-			'sharedWith' => 'recipient',
1135
-			'sharedBy' => 'initiator',
1136
-			'owner' => 'currentUser',
1137
-			'node' => $file1,
1138
-			'id' => 423,
1139
-		];
1140
-
1141
-		$file1CircleShareOwnerExpected = [
1142
-			'id' => 423,
1143
-			'share_type' => IShare::TYPE_CIRCLE,
1144
-		];
1145
-
1146
-		$file1RoomShareOwner = [
1147
-			'type' => IShare::TYPE_ROOM,
1148
-			'sharedWith' => 'recipient',
1149
-			'sharedBy' => 'initiator',
1150
-			'owner' => 'currentUser',
1151
-			'node' => $file1,
1152
-			'id' => 442,
1153
-		];
1154
-
1155
-		$file1RoomShareOwnerExpected = [
1156
-			'id' => 442,
1157
-			'share_type' => IShare::TYPE_ROOM,
1158
-		];
1159
-
1160
-		$file1RemoteShareOwner = [
1161
-			'type' => IShare::TYPE_REMOTE,
1162
-			'sharedWith' => 'recipient',
1163
-			'sharedBy' => 'initiator',
1164
-			'owner' => 'currentUser',
1165
-			'expirationDate' => new \DateTime('2000-01-01T01:02:03'),
1166
-			'node' => $file1,
1167
-			'id' => 815,
1168
-		];
1169
-
1170
-		$file1RemoteShareOwnerExpected = [
1171
-			'id' => 815,
1172
-			'share_type' => IShare::TYPE_REMOTE,
1173
-		];
1174
-
1175
-		$file1RemoteGroupShareOwner = [
1176
-			'type' => IShare::TYPE_REMOTE_GROUP,
1177
-			'sharedWith' => 'recipient',
1178
-			'sharedBy' => 'initiator',
1179
-			'owner' => 'currentUser',
1180
-			'expirationDate' => new \DateTime('2000-01-01T01:02:03'),
1181
-			'node' => $file1,
1182
-			'id' => 816,
1183
-		];
1184
-
1185
-		$file1RemoteGroupShareOwnerExpected = [
1186
-			'id' => 816,
1187
-			'share_type' => IShare::TYPE_REMOTE_GROUP,
1188
-		];
1189
-
1190
-		$file2UserShareOwner = [
1191
-			'type' => IShare::TYPE_USER,
1192
-			'sharedWith' => 'recipient',
1193
-			'sharedBy' => 'initiator',
1194
-			'owner' => 'currentUser',
1195
-			'node' => $file2,
1196
-			'id' => 823,
1197
-		];
1198
-
1199
-		$file2UserShareOwnerExpected = [
1200
-			'id' => 823,
1201
-			'share_type' => IShare::TYPE_USER,
1202
-		];
1203
-
1204
-		$data = [
1205
-			[
1206
-				[
1207
-					'node' => $file1,
1208
-				],
1209
-				[
1210
-					'file1' => [
1211
-						IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareOwner, $file1UserShareOwner],
1212
-					],
1213
-				],
1214
-				[
1215
-				],
1216
-				[
1217
-					$file1UserShareOwnerExpected
1218
-				]
1219
-			],
1220
-			[
1221
-				[
1222
-					'node' => $file1,
1223
-				],
1224
-				[
1225
-					'file1' => [
1226
-						IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareRecipient],
1227
-					],
1228
-				],
1229
-				[
1230
-				],
1231
-				[
1232
-					$file1UserShareOwnerExpected,
1233
-				]
1234
-			],
1235
-			[
1236
-				[
1237
-					'node' => $file1,
1238
-				],
1239
-				[
1240
-					'file1' => [
1241
-						IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1242
-					],
1243
-				],
1244
-				[
1245
-				],
1246
-				[
1247
-					$file1UserShareOwnerExpected,
1248
-					$file1UserShareInitiatorExpected,
1249
-					$file1UserShareOtherExpected,
1250
-				]
1251
-			],
1252
-			[
1253
-				[
1254
-					'node' => $file1,
1255
-				],
1256
-				[
1257
-					'file1' => [
1258
-						IShare::TYPE_USER => [$file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1259
-					],
1260
-				],
1261
-				[
1262
-				],
1263
-				[
1264
-					$file1UserShareInitiatorExpected,
1265
-				]
1266
-			],
1267
-			[
1268
-				[
1269
-					'node' => $file1,
1270
-				],
1271
-				[
1272
-					'file1' => [
1273
-						IShare::TYPE_USER => [$file1UserShareOwner],
1274
-						IShare::TYPE_GROUP => [$file1GroupShareRecipient],
1275
-					],
1276
-				],
1277
-				[
1278
-				],
1279
-				[
1280
-					$file1UserShareOwnerExpected,
1281
-					$file1GroupShareRecipientExpected,
1282
-				]
1283
-			],
1284
-			[
1285
-				[
1286
-					'node' => $file1,
1287
-				],
1288
-				[
1289
-					'file1' => [
1290
-						IShare::TYPE_USER => [$file1UserShareOwner],
1291
-						IShare::TYPE_GROUP => [$file1GroupShareOwner],
1292
-						IShare::TYPE_LINK => [$file1LinkShareOwner],
1293
-						IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1294
-						IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1295
-						IShare::TYPE_ROOM => [$file1RoomShareOwner],
1296
-						IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1297
-						IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1298
-					],
1299
-				],
1300
-				[
1301
-				],
1302
-				[
1303
-					$file1UserShareOwnerExpected,
1304
-					$file1GroupShareOwnerExpected,
1305
-					$file1LinkShareOwnerExpected,
1306
-					$file1EmailShareOwnerExpected,
1307
-					$file1CircleShareOwnerExpected,
1308
-					$file1RoomShareOwnerExpected,
1309
-				]
1310
-			],
1311
-			[
1312
-				[
1313
-					'node' => $file1,
1314
-				],
1315
-				[
1316
-					'file1' => [
1317
-						IShare::TYPE_USER => [$file1UserShareOwner],
1318
-						IShare::TYPE_GROUP => [$file1GroupShareOwner],
1319
-						IShare::TYPE_LINK => [$file1LinkShareOwner],
1320
-						IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1321
-						IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1322
-						IShare::TYPE_ROOM => [$file1RoomShareOwner],
1323
-						IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1324
-						IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1325
-					],
1326
-				],
1327
-				[
1328
-					IShare::TYPE_REMOTE => true,
1329
-					IShare::TYPE_REMOTE_GROUP => true,
1330
-				],
1331
-				[
1332
-					$file1UserShareOwnerExpected,
1333
-					$file1GroupShareOwnerExpected,
1334
-					$file1LinkShareOwnerExpected,
1335
-					$file1EmailShareOwnerExpected,
1336
-					$file1CircleShareOwnerExpected,
1337
-					$file1RoomShareOwnerExpected,
1338
-					$file1RemoteShareOwnerExpected,
1339
-					$file1RemoteGroupShareOwnerExpected,
1340
-				]
1341
-			],
1342
-			[
1343
-				[
1344
-					'node' => $folder,
1345
-					'subfiles' => 'true',
1346
-				],
1347
-				[
1348
-					'file1' => [
1349
-						IShare::TYPE_USER => [$file1UserShareOwner],
1350
-					],
1351
-					'file2' => [
1352
-						IShare::TYPE_USER => [$file2UserShareOwner],
1353
-					],
1354
-				],
1355
-				[
1356
-				],
1357
-				[
1358
-					$file1UserShareOwnerExpected,
1359
-					$file2UserShareOwnerExpected,
1360
-				]
1361
-			],
1362
-			[
1363
-				[
1364
-					'node' => $folder,
1365
-					'subfiles' => 'true',
1366
-				],
1367
-				[
1368
-					'file1' => [
1369
-						IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareOwner, $file1UserShareOwner],
1370
-					],
1371
-				],
1372
-				[
1373
-				],
1374
-				[
1375
-					$file1UserShareOwnerExpected,
1376
-				]
1377
-			],
1378
-			[
1379
-				[
1380
-					'node' => $folder,
1381
-					'subfiles' => 'true',
1382
-				],
1383
-				[
1384
-					'file1' => [
1385
-						IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareRecipient],
1386
-					],
1387
-				],
1388
-				[
1389
-				],
1390
-				[
1391
-					$file1UserShareOwnerExpected
1392
-				]
1393
-			],
1394
-			[
1395
-				[
1396
-					'node' => $folder,
1397
-					'subfiles' => 'true',
1398
-				],
1399
-				[
1400
-					'file1' => [
1401
-						IShare::TYPE_USER => [$file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1402
-					],
1403
-					'file2' => [
1404
-						IShare::TYPE_USER => [$file2UserShareOwner],
1405
-					],
1406
-				],
1407
-				[
1408
-				],
1409
-				[
1410
-					$file1UserShareInitiatorExpected,
1411
-					$file1UserShareOtherExpected,
1412
-					$file2UserShareOwnerExpected,
1413
-				]
1414
-			],
1415
-			// This might not happen in a real environment, as the combination
1416
-			// of shares does not seem to be possible on a folder without
1417
-			// resharing rights; if the folder has resharing rights then the
1418
-			// share with others would be included too in the results.
1419
-			[
1420
-				[
1421
-					'node' => $folder,
1422
-					'subfiles' => 'true',
1423
-				],
1424
-				[
1425
-					'file1' => [
1426
-						IShare::TYPE_USER => [$file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1427
-					],
1428
-				],
1429
-				[
1430
-				],
1431
-				[
1432
-					$file1UserShareInitiatorExpected,
1433
-				]
1434
-			],
1435
-			[
1436
-				[
1437
-					'node' => $folder,
1438
-					'subfiles' => 'true',
1439
-				],
1440
-				[
1441
-					'file1' => [
1442
-						IShare::TYPE_USER => [$file1UserShareOwner],
1443
-						IShare::TYPE_GROUP => [$file1GroupShareRecipient],
1444
-					],
1445
-				],
1446
-				[
1447
-				],
1448
-				[
1449
-					$file1UserShareOwnerExpected,
1450
-					$file1GroupShareRecipientExpected,
1451
-				]
1452
-			],
1453
-			[
1454
-				[
1455
-					'node' => $folder,
1456
-					'subfiles' => 'true',
1457
-				],
1458
-				[
1459
-					'file1' => [
1460
-						IShare::TYPE_USER => [$file1UserShareOwner],
1461
-						IShare::TYPE_GROUP => [$file1GroupShareOwner],
1462
-						IShare::TYPE_LINK => [$file1LinkShareOwner],
1463
-						IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1464
-						IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1465
-						IShare::TYPE_ROOM => [$file1RoomShareOwner],
1466
-						IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1467
-						IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1468
-					],
1469
-				],
1470
-				[
1471
-				],
1472
-				[
1473
-					$file1UserShareOwnerExpected,
1474
-					$file1GroupShareOwnerExpected,
1475
-					$file1LinkShareOwnerExpected,
1476
-					$file1EmailShareOwnerExpected,
1477
-					$file1CircleShareOwnerExpected,
1478
-					$file1RoomShareOwnerExpected,
1479
-				]
1480
-			],
1481
-			[
1482
-				[
1483
-					'node' => $folder,
1484
-					'subfiles' => 'true',
1485
-				],
1486
-				[
1487
-					'file1' => [
1488
-						IShare::TYPE_USER => [$file1UserShareOwner],
1489
-						IShare::TYPE_GROUP => [$file1GroupShareOwner],
1490
-						IShare::TYPE_LINK => [$file1LinkShareOwner],
1491
-						IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1492
-						IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1493
-						IShare::TYPE_ROOM => [$file1RoomShareOwner],
1494
-						IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1495
-						IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1496
-					],
1497
-				],
1498
-				[
1499
-					IShare::TYPE_REMOTE => true,
1500
-					IShare::TYPE_REMOTE_GROUP => true,
1501
-				],
1502
-				[
1503
-					$file1UserShareOwnerExpected,
1504
-					$file1GroupShareOwnerExpected,
1505
-					$file1LinkShareOwnerExpected,
1506
-					$file1EmailShareOwnerExpected,
1507
-					$file1CircleShareOwnerExpected,
1508
-					$file1RoomShareOwnerExpected,
1509
-					$file1RemoteShareOwnerExpected,
1510
-					$file1RemoteGroupShareOwnerExpected,
1511
-				]
1512
-			],
1513
-		];
1514
-
1515
-		return $data;
1516
-	}
1517
-
1518
-	private function mockSimpleNode(string $class, array $methods): MockObject {
1519
-		$node = $this->createMock($class);
1520
-		foreach ($methods as $method => $return) {
1521
-			if ($method === 'getDirectoryListing') {
1522
-				$return = array_map(
1523
-					fn ($nodeParams) => $this->mockSimpleNode(...$nodeParams),
1524
-					$return
1525
-				);
1526
-			}
1527
-			$node->method($method)->willReturn($return);
1528
-		}
1529
-		return $node;
1530
-	}
1531
-
1532
-	#[DataProvider('dataGetShares')]
1533
-	public function testGetShares(array $getSharesParameters, array $shares, array $extraShareTypes, array $expected): void {
1534
-		$shares = array_map(
1535
-			fn ($sharesByType) => array_map(
1536
-				fn ($shareList) => array_map(
1537
-					function (array $shareParams): IShare {
1538
-						$share = Server::get(IManager::class)->newShare();
1539
-						$share->setShareType($shareParams['type'])
1540
-							->setSharedBy($shareParams['sharedBy'])
1541
-							->setShareOwner($shareParams['owner'])
1542
-							->setPermissions(Constants::PERMISSION_READ)
1543
-							->setId($shareParams['id']);
1544
-						if (isset($shareParams['sharedWith'])) {
1545
-							$share->setSharedWith($shareParams['sharedWith']);
1546
-						}
1547
-						if (isset($shareParams['sharedWithDisplayName'])) {
1548
-							$share->setSharedWithDisplayName($shareParams['sharedWithDisplayName']);
1549
-						}
1550
-						if (isset($shareParams['sharedWithAvatar'])) {
1551
-							$share->setSharedWithAvatar($shareParams['sharedWithAvatar']);
1552
-						}
1553
-						if (isset($shareParams['attributes'])) {
1554
-							$shareAttributes = $this->createMock(IShareAttributes::class);
1555
-							$shareAttributes->method('toArray')->willReturn($shareParams['attributes']);
1556
-							$shareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
1557
-							$share->setAttributes($shareAttributes);
1558
-
1559
-							$expects['attributes'] = \json_encode($shareParams['attributes']);
1560
-						}
1561
-						if (isset($shareParams['node'])) {
1562
-							$node = $this->mockSimpleNode(...$shareParams['node']);
1563
-							$share->setNode($node);
1564
-						}
1565
-						if (isset($shareParams['note'])) {
1566
-							$share->setNote($shareParams['note']);
1567
-						}
1568
-						if (isset($shareParams['expirationDate'])) {
1569
-							$share->setExpirationDate($shareParams['expirationDate']);
1570
-						}
1571
-						if (isset($shareParams['token'])) {
1572
-							$share->setToken($shareParams['token']);
1573
-						}
1574
-						if (isset($shareParams['label'])) {
1575
-							$share->setLabel($shareParams['label']);
1576
-						}
1577
-						if (isset($shareParams['password'])) {
1578
-							$share->setPassword($shareParams['password']);
1579
-						}
1580
-						if (isset($shareParams['sendPasswordByTalk'])) {
1581
-							$share->setSendPasswordByTalk($shareParams['sendPasswordByTalk']);
1582
-						}
1583
-						return $share;
1584
-					},
1585
-					$shareList
1586
-				),
1587
-				$sharesByType
1588
-			),
1589
-			$shares
1590
-		);
1591
-
1592
-		/** @var ShareAPIController&MockObject $ocs */
1593
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
1594
-			->setConstructorArgs([
1595
-				$this->appName,
1596
-				$this->request,
1597
-				$this->shareManager,
1598
-				$this->groupManager,
1599
-				$this->userManager,
1600
-				$this->rootFolder,
1601
-				$this->urlGenerator,
1602
-				$this->l,
1603
-				$this->config,
1604
-				$this->appConfig,
1605
-				$this->appManager,
1606
-				$this->serverContainer,
1607
-				$this->userStatusManager,
1608
-				$this->previewManager,
1609
-				$this->dateTimeZone,
1610
-				$this->logger,
1611
-				$this->factory,
1612
-				$this->mailer,
1613
-				$this->tagManager,
1614
-				$this->getEmailValidatorWithStrictEmailCheck(),
1615
-				$this->trustedServers,
1616
-				$this->currentUser,
1617
-			])
1618
-			->onlyMethods(['formatShare'])
1619
-			->getMock();
1620
-
1621
-		$ocs->method('formatShare')
1622
-			->willReturnCallback(
1623
-				function ($share) {
1624
-					return [
1625
-						'id' => $share->getId(),
1626
-						'share_type' => $share->getShareType()
1627
-					];
1628
-				}
1629
-			);
1630
-
1631
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
1632
-		$userFolder->method('get')
1633
-			->with('path')
1634
-			->willReturn($this->mockSimpleNode(...$getSharesParameters['node']));
1635
-
1636
-		$this->rootFolder->method('getUserFolder')
1637
-			->with($this->currentUser)
1638
-			->willReturn($userFolder);
1639
-
1640
-		$this->shareManager
1641
-			->method('getSharesBy')
1642
-			->willReturnCallback(
1643
-				function ($user, $shareType, $node) use ($shares) {
1644
-					if (!isset($shares[$node->getName()]) || !isset($shares[$node->getName()][$shareType])) {
1645
-						return [];
1646
-					}
1647
-					return $shares[$node->getName()][$shareType];
1648
-				}
1649
-			);
1650
-
1651
-		$this->shareManager
1652
-			->method('outgoingServer2ServerSharesAllowed')
1653
-			->willReturn($extraShareTypes[ISHARE::TYPE_REMOTE] ?? false);
1654
-
1655
-		$this->shareManager
1656
-			->method('outgoingServer2ServerGroupSharesAllowed')
1657
-			->willReturn($extraShareTypes[ISHARE::TYPE_REMOTE_GROUP] ?? false);
1658
-
1659
-		$this->groupManager
1660
-			->method('isInGroup')
1661
-			->willReturnCallback(
1662
-				function ($user, $group) {
1663
-					return $group === 'currentUserGroup';
1664
-				}
1665
-			);
1666
-
1667
-		$result = $ocs->getShares(
1668
-			$getSharesParameters['sharedWithMe'] ?? 'false',
1669
-			$getSharesParameters['reshares'] ?? 'false',
1670
-			$getSharesParameters['subfiles'] ?? 'false',
1671
-			'path'
1672
-		);
1673
-
1674
-		$this->assertEquals($expected, $result->getData());
1675
-	}
1676
-
1677
-	public function testCanAccessShareAsOwner(): void {
1678
-		$share = $this->createMock(IShare::class);
1679
-		$share->method('getShareOwner')->willReturn($this->currentUser);
1680
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1681
-	}
1682
-
1683
-	public function testCanAccessShareAsSharer(): void {
1684
-		$share = $this->createMock(IShare::class);
1685
-		$share->method('getSharedBy')->willReturn($this->currentUser);
1686
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1687
-	}
1688
-
1689
-	public function testCanAccessShareAsSharee(): void {
1690
-		$share = $this->createMock(IShare::class);
1691
-		$share->method('getShareType')->willReturn(IShare::TYPE_USER);
1692
-		$share->method('getSharedWith')->willReturn($this->currentUser);
1693
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1694
-	}
1695
-
1696
-	public function testCannotAccessLinkShare(): void {
1697
-		$share = $this->createMock(IShare::class);
1698
-		$share->method('getShareType')->willReturn(IShare::TYPE_LINK);
1699
-		$share->method('getNodeId')->willReturn(42);
1700
-
1701
-		$userFolder = $this->createMock(Folder::class);
1702
-		$this->rootFolder->method('getUserFolder')
1703
-			->with($this->currentUser)
1704
-			->willReturn($userFolder);
1705
-
1706
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1707
-	}
1708
-
1709
-	#[DataProvider('dataCanAccessShareWithPermissions')]
1710
-	public function testCanAccessShareWithPermissions(int $permissions, bool $expected): void {
1711
-		$share = $this->createMock(IShare::class);
1712
-		$share->method('getShareType')->willReturn(IShare::TYPE_USER);
1713
-		$share->method('getSharedWith')->willReturn($this->createMock(IUser::class));
1714
-		$share->method('getNodeId')->willReturn(42);
1715
-
1716
-		$file = $this->createMock(File::class);
1717
-
1718
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
1719
-		$userFolder->method('getFirstNodeById')
1720
-			->with($share->getNodeId())
1721
-			->willReturn($file);
1722
-		$userFolder->method('getById')
1723
-			->with($share->getNodeId())
1724
-			->willReturn([$file]);
1725
-		$this->rootFolder->method('getUserFolder')
1726
-			->with($this->currentUser)
1727
-			->willReturn($userFolder);
1728
-
1729
-		$file->method('getPermissions')
1730
-			->willReturn($permissions);
1731
-
1732
-		if ($expected) {
1733
-			$this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1734
-		} else {
1735
-			$this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1736
-		}
1737
-	}
1738
-
1739
-	public static function dataCanAccessShareWithPermissions(): array {
1740
-		return [
1741
-			[Constants::PERMISSION_SHARE, true],
1742
-			[Constants::PERMISSION_READ, false],
1743
-			[Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, true],
1744
-		];
1745
-	}
1746
-
1747
-	#[DataProvider('dataCanAccessShareAsGroupMember')]
1748
-	public function testCanAccessShareAsGroupMember(string $group, bool $expected): void {
1749
-		$share = $this->createMock(IShare::class);
1750
-		$share->method('getShareType')->willReturn(IShare::TYPE_GROUP);
1751
-		$share->method('getSharedWith')->willReturn($group);
1752
-		$share->method('getNodeId')->willReturn(42);
1753
-
1754
-		$file = $this->createMock(File::class);
1755
-
1756
-		$userFolder = $this->createMock(Folder::class);
1757
-		$userFolder->method('getFirstNodeById')
1758
-			->with($share->getNodeId())
1759
-			->willReturn($file);
1760
-		$userFolder->method('getById')
1761
-			->with($share->getNodeId())
1762
-			->willReturn([$file]);
1763
-		$this->rootFolder->method('getUserFolder')
1764
-			->with($this->currentUser)
1765
-			->willReturn($userFolder);
1766
-
1767
-		$user = $this->createMock(IUser::class);
1768
-		$this->userManager->method('get')
1769
-			->with($this->currentUser)
1770
-			->willReturn($user);
1771
-
1772
-		$group = $this->createMock(IGroup::class);
1773
-		$group->method('inGroup')->with($user)->willReturn(true);
1774
-		$group2 = $this->createMock(IGroup::class);
1775
-		$group2->method('inGroup')->with($user)->willReturn(false);
1776
-
1777
-		$this->groupManager->method('get')->willReturnMap([
1778
-			['group', $group],
1779
-			['group2', $group2],
1780
-			['group-null', null],
1781
-		]);
1782
-
1783
-		if ($expected) {
1784
-			$this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1785
-		} else {
1786
-			$this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1787
-		}
1788
-	}
1789
-
1790
-	public static function dataCanAccessShareAsGroupMember(): array {
1791
-		return [
1792
-			['group', true],
1793
-			['group2', false],
1794
-			['group-null', false],
1795
-		];
1796
-	}
1797
-
1798
-	public static function dataCanAccessRoomShare(): array {
1799
-		return [
1800
-			[false, false, false],
1801
-			[false, false, true],
1802
-			[true, true, true],
1803
-			[false, true, false],
1804
-		];
1805
-	}
1806
-
1807
-	#[DataProvider('dataCanAccessRoomShare')]
1808
-	public function testCanAccessRoomShare(
1809
-		bool $expected,
1810
-		bool $helperAvailable,
1811
-		bool $canAccessShareByHelper,
1812
-	): void {
1813
-		$share = $this->createMock(IShare::class);
1814
-		$share->method('getShareType')->willReturn(IShare::TYPE_ROOM);
1815
-		$share->method('getSharedWith')->willReturn('recipientRoom');
1816
-
1817
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
1818
-		$this->rootFolder->method('getUserFolder')
1819
-			->with($this->currentUser)
1820
-			->willReturn($userFolder);
1821
-
1822
-		$userFolder->method('getById')
1823
-			->with($share->getNodeId())
1824
-			->willReturn([$share->getNode()]);
1825
-
1826
-		if (!$helperAvailable) {
1827
-			$this->appManager->method('isEnabledForUser')
1828
-				->with('spreed')
1829
-				->willReturn(false);
1830
-		} else {
1831
-			$this->appManager->method('isEnabledForUser')
1832
-				->with('spreed')
1833
-				->willReturn(true);
1834
-
1835
-			// This is not possible anymore with PHPUnit 10+
1836
-			// as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
1837
-			// $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
1838
-			$helper = $this->getMockBuilder(\stdClass::class)
1839
-				->addMethods(['canAccessShare'])
1840
-				->getMock();
1841
-			$helper->method('canAccessShare')
1842
-				->with($share, $this->currentUser)
1843
-				->willReturn($canAccessShareByHelper);
1844
-
1845
-			$this->serverContainer->method('get')
1846
-				->with('\OCA\Talk\Share\Helper\ShareAPIController')
1847
-				->willReturn($helper);
1848
-		}
1849
-
1850
-		$this->assertEquals($expected, $this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1851
-	}
1852
-
1853
-
1854
-	public function testCreateShareNoPath(): void {
1855
-		$this->expectException(OCSNotFoundException::class);
1856
-		$this->expectExceptionMessage('Please specify a file or folder path');
1857
-
1858
-		$this->ocs->createShare();
1859
-	}
1860
-
1861
-
1862
-	public function testCreateShareInvalidPath(): void {
1863
-		$this->expectException(OCSNotFoundException::class);
1864
-		$this->expectExceptionMessage('Wrong path, file/folder does not exist');
1865
-
1866
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
1867
-		$this->rootFolder->expects($this->once())
1868
-			->method('getUserFolder')
1869
-			->with('currentUser')
1870
-			->willReturn($userFolder);
1871
-
1872
-		$userFolder->expects($this->once())
1873
-			->method('get')
1874
-			->with('invalid-path')
1875
-			->willThrowException(new NotFoundException());
1876
-
1877
-		$this->ocs->createShare('invalid-path');
1878
-	}
1879
-
1880
-	public function testCreateShareInvalidShareType(): void {
1881
-		$this->expectException(OCSBadRequestException::class);
1882
-		$this->expectExceptionMessage('Unknown share type');
1883
-
1884
-		$share = $this->newShare();
1885
-		$this->shareManager->method('newShare')->willReturn($share);
1886
-
1887
-		[$userFolder, $file] = $this->getNonSharedUserFile();
1888
-		$this->rootFolder->expects($this->atLeastOnce())
1889
-			->method('getUserFolder')
1890
-			->with('currentUser')
1891
-			->willReturn($userFolder);
1892
-
1893
-		$userFolder->expects($this->atLeastOnce())
1894
-			->method('get')
1895
-			->with('valid-path')
1896
-			->willReturn($file);
1897
-		$userFolder->method('getById')
1898
-			->willReturn([]);
1899
-
1900
-		$file->expects($this->once())
1901
-			->method('lock')
1902
-			->with(ILockingProvider::LOCK_SHARED);
1903
-
1904
-		$this->ocs->createShare('valid-path', 31);
1905
-	}
1906
-
1907
-	public function testCreateShareUserNoShareWith(): void {
1908
-		$this->expectException(OCSNotFoundException::class);
1909
-		$this->expectExceptionMessage('Please specify a valid account to share with');
1910
-
1911
-		$share = $this->newShare();
1912
-		$this->shareManager->method('newShare')->willReturn($share);
1913
-
1914
-		[$userFolder, $path] = $this->getNonSharedUserFile();
1915
-		$this->rootFolder->method('getUserFolder')
1916
-			->with('currentUser')
1917
-			->willReturn($userFolder);
1918
-
1919
-		$userFolder->expects($this->once())
1920
-			->method('get')
1921
-			->with('valid-path')
1922
-			->willReturn($path);
1923
-		$userFolder->method('getById')
1924
-			->willReturn([]);
1925
-
1926
-		$path->expects($this->once())
1927
-			->method('lock')
1928
-			->with(ILockingProvider::LOCK_SHARED);
1929
-
1930
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER);
1931
-	}
1932
-
1933
-
1934
-	public function testCreateShareUserNoValidShareWith(): void {
1935
-		$this->expectException(OCSNotFoundException::class);
1936
-		$this->expectExceptionMessage('Please specify a valid account to share with');
1937
-
1938
-		$share = $this->newShare();
1939
-		$this->shareManager->method('newShare')->willReturn($share);
1940
-
1941
-		[$userFolder, $path] = $this->getNonSharedUserFile();
1942
-		$this->rootFolder->method('getUserFolder')
1943
-			->with('currentUser')
1944
-			->willReturn($userFolder);
1945
-
1946
-		$userFolder->expects($this->once())
1947
-			->method('get')
1948
-			->with('valid-path')
1949
-			->willReturn($path);
1950
-		$userFolder->method('getById')
1951
-			->willReturn([]);
1952
-		$path->expects($this->once())
1953
-			->method('lock')
1954
-			->with(ILockingProvider::LOCK_SHARED);
1955
-		$this->userManager->method('userExists')
1956
-			->with('invalidUser')
1957
-			->willReturn(false);
1958
-
1959
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER, 'invalidUser');
1960
-	}
1961
-
1962
-	public function testCreateShareUser(): void {
1963
-		$share = $this->newShare();
1964
-		$this->shareManager->method('newShare')->willReturn($share);
1965
-
1966
-		/** @var ShareAPIController $ocs */
1967
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
1968
-			->setConstructorArgs([
1969
-				$this->appName,
1970
-				$this->request,
1971
-				$this->shareManager,
1972
-				$this->groupManager,
1973
-				$this->userManager,
1974
-				$this->rootFolder,
1975
-				$this->urlGenerator,
1976
-				$this->l,
1977
-				$this->config,
1978
-				$this->appConfig,
1979
-				$this->appManager,
1980
-				$this->serverContainer,
1981
-				$this->userStatusManager,
1982
-				$this->previewManager,
1983
-				$this->dateTimeZone,
1984
-				$this->logger,
1985
-				$this->factory,
1986
-				$this->mailer,
1987
-				$this->tagManager,
1988
-				$this->getEmailValidatorWithStrictEmailCheck(),
1989
-				$this->trustedServers,
1990
-				$this->currentUser,
1991
-			])->onlyMethods(['formatShare'])
1992
-			->getMock();
1993
-
1994
-		[$userFolder, $path] = $this->getNonSharedUserFile();
1995
-		$this->rootFolder->expects($this->exactly(2))
1996
-			->method('getUserFolder')
1997
-			->with('currentUser')
1998
-			->willReturn($userFolder);
1999
-
2000
-		$userFolder->expects($this->once())
2001
-			->method('get')
2002
-			->with('valid-path')
2003
-			->willReturn($path);
2004
-		$userFolder->method('getById')
2005
-			->willReturn([]);
2006
-
2007
-		$this->userManager->method('userExists')->with('validUser')->willReturn(true);
2008
-
2009
-		$path->expects($this->once())
2010
-			->method('lock')
2011
-			->with(ILockingProvider::LOCK_SHARED);
2012
-
2013
-		$this->shareManager->method('createShare')
2014
-			->with($this->callback(function (IShare $share) use ($path) {
2015
-				return $share->getNode() === $path
2016
-					&& $share->getPermissions() === (
2017
-						Constants::PERMISSION_ALL
2018
-						& ~Constants::PERMISSION_DELETE
2019
-						& ~Constants::PERMISSION_CREATE
2020
-					)
2021
-					&& $share->getShareType() === IShare::TYPE_USER
2022
-					&& $share->getSharedWith() === 'validUser'
2023
-					&& $share->getSharedBy() === 'currentUser';
2024
-			}))
2025
-			->willReturnArgument(0);
2026
-
2027
-		$expected = new DataResponse([]);
2028
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER, 'validUser');
2029
-
2030
-		$this->assertInstanceOf(get_class($expected), $result);
2031
-		$this->assertEquals($expected->getData(), $result->getData());
2032
-	}
2033
-
2034
-
2035
-	public function testCreateShareGroupNoValidShareWith(): void {
2036
-		$this->expectException(OCSNotFoundException::class);
2037
-		$this->expectExceptionMessage('Please specify a valid group');
2038
-
2039
-		$share = $this->newShare();
2040
-		$this->shareManager->method('newShare')->willReturn($share);
2041
-		$this->shareManager->method('createShare')->willReturnArgument(0);
2042
-		$this->shareManager->method('allowGroupSharing')->willReturn(true);
2043
-
2044
-		[$userFolder, $path] = $this->getNonSharedUserFile();
2045
-		$this->rootFolder->method('getUserFolder')
2046
-			->with('currentUser')
2047
-			->willReturn($userFolder);
2048
-
2049
-		$userFolder->expects($this->once())
2050
-			->method('get')
2051
-			->with('valid-path')
2052
-			->willReturn($path);
2053
-		$userFolder->method('getById')
2054
-			->willReturn([]);
2055
-
2056
-		$path->expects($this->once())
2057
-			->method('lock')
2058
-			->with(ILockingProvider::LOCK_SHARED);
2059
-
2060
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_GROUP, 'invalidGroup');
2061
-	}
2062
-
2063
-	public function testCreateShareGroup(): void {
2064
-		$share = $this->newShare();
2065
-		$this->shareManager->method('newShare')->willReturn($share);
2066
-
2067
-		/** @var ShareAPIController&MockObject $ocs */
2068
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
2069
-			->setConstructorArgs([
2070
-				$this->appName,
2071
-				$this->request,
2072
-				$this->shareManager,
2073
-				$this->groupManager,
2074
-				$this->userManager,
2075
-				$this->rootFolder,
2076
-				$this->urlGenerator,
2077
-				$this->l,
2078
-				$this->config,
2079
-				$this->appConfig,
2080
-				$this->appManager,
2081
-				$this->serverContainer,
2082
-				$this->userStatusManager,
2083
-				$this->previewManager,
2084
-				$this->dateTimeZone,
2085
-				$this->logger,
2086
-				$this->factory,
2087
-				$this->mailer,
2088
-				$this->tagManager,
2089
-				$this->getEmailValidatorWithStrictEmailCheck(),
2090
-				$this->trustedServers,
2091
-				$this->currentUser,
2092
-			])->onlyMethods(['formatShare'])
2093
-			->getMock();
2094
-
2095
-		$this->request
2096
-			->method('getParam')
2097
-			->willReturnMap([
2098
-				['path', null, 'valid-path'],
2099
-				['permissions', null, Constants::PERMISSION_ALL],
2100
-				['shareType', '-1', IShare::TYPE_GROUP],
2101
-				['shareWith', null, 'validGroup'],
2102
-			]);
2103
-
2104
-		[$userFolder, $path] = $this->getNonSharedUserFolder();
2105
-		$this->rootFolder->expects($this->exactly(2))
2106
-			->method('getUserFolder')
2107
-			->with('currentUser')
2108
-			->willReturn($userFolder);
2109
-
2110
-		$userFolder->expects($this->once())
2111
-			->method('get')
2112
-			->with('valid-path')
2113
-			->willReturn($path);
2114
-		$userFolder->method('getById')
2115
-			->willReturn([]);
2116
-
2117
-		$this->groupManager->method('groupExists')->with('validGroup')->willReturn(true);
2118
-
2119
-		$this->shareManager->expects($this->once())
2120
-			->method('allowGroupSharing')
2121
-			->willReturn(true);
2122
-
2123
-		$path->expects($this->once())
2124
-			->method('lock')
2125
-			->with(ILockingProvider::LOCK_SHARED);
2126
-
2127
-		$this->shareManager->method('createShare')
2128
-			->with($this->callback(function (IShare $share) use ($path) {
2129
-				return $share->getNode() === $path
2130
-				&& $share->getPermissions() === Constants::PERMISSION_ALL
2131
-				&& $share->getShareType() === IShare::TYPE_GROUP
2132
-				&& $share->getSharedWith() === 'validGroup'
2133
-				&& $share->getSharedBy() === 'currentUser';
2134
-			}))
2135
-			->willReturnArgument(0);
2136
-
2137
-		$expected = new DataResponse([]);
2138
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_GROUP, 'validGroup');
2139
-
2140
-		$this->assertInstanceOf(get_class($expected), $result);
2141
-		$this->assertEquals($expected->getData(), $result->getData());
2142
-	}
2143
-
2144
-
2145
-	public function testCreateShareGroupNotAllowed(): void {
2146
-		$this->expectException(OCSNotFoundException::class);
2147
-		$this->expectExceptionMessage('Group sharing is disabled by the administrator');
2148
-
2149
-		$share = $this->newShare();
2150
-		$this->shareManager->method('newShare')->willReturn($share);
2151
-
2152
-		[$userFolder, $path] = $this->getNonSharedUserFolder();
2153
-		$this->rootFolder->method('getUserFolder')
2154
-			->with('currentUser')
2155
-			->willReturn($userFolder);
2156
-
2157
-		$userFolder->expects($this->once())
2158
-			->method('get')
2159
-			->with('valid-path')
2160
-			->willReturn($path);
2161
-		$userFolder->method('getById')
2162
-			->willReturn([]);
2163
-
2164
-		$this->groupManager->method('groupExists')->with('validGroup')->willReturn(true);
2165
-
2166
-		$this->shareManager->expects($this->once())
2167
-			->method('allowGroupSharing')
2168
-			->willReturn(false);
2169
-
2170
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_GROUP, 'invalidGroup');
2171
-	}
2172
-
2173
-
2174
-	public function testCreateShareLinkNoLinksAllowed(): void {
2175
-		$this->expectException(OCSNotFoundException::class);
2176
-		$this->expectExceptionMessage('Public link sharing is disabled by the administrator');
2177
-
2178
-		$this->request
2179
-			->method('getParam')
2180
-			->willReturnMap([
2181
-				['path', null, 'valid-path'],
2182
-				['shareType', '-1', IShare::TYPE_LINK],
2183
-			]);
2184
-
2185
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2186
-		$path->method('getId')->willReturn(42);
2187
-		$storage = $this->createMock(IStorage::class);
2188
-		$storage->method('instanceOfStorage')
2189
-			->willReturnMap([
2190
-				['OCA\Files_Sharing\External\Storage', false],
2191
-				['OCA\Files_Sharing\SharedStorage', false],
2192
-			]);
2193
-		$path->method('getStorage')->willReturn($storage);
2194
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2195
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2196
-		$this->rootFolder->method('getById')
2197
-			->willReturn([]);
2198
-
2199
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2200
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2201
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(false);
2202
-
2203
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK);
2204
-	}
2205
-
2206
-
2207
-	public function testCreateShareLinkNoPublicUpload(): void {
2208
-		$this->expectException(OCSForbiddenException::class);
2209
-		$this->expectExceptionMessage('Public upload disabled by the administrator');
2210
-
2211
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2212
-		$path->method('getId')->willReturn(42);
2213
-		$storage = $this->createMock(IStorage::class);
2214
-		$storage->method('instanceOfStorage')
2215
-			->willReturnMap([
2216
-				['OCA\Files_Sharing\External\Storage', false],
2217
-				['OCA\Files_Sharing\SharedStorage', false],
2218
-			]);
2219
-		$path->method('getStorage')->willReturn($storage);
2220
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2221
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2222
-		$this->rootFolder->method('getById')
2223
-			->willReturn([]);
2224
-
2225
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2226
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2227
-
2228
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true');
2229
-	}
2230
-
2231
-
2232
-	public function testCreateShareLinkPublicUploadFile(): void {
2233
-		$this->expectException(OCSBadRequestException::class);
2234
-		$this->expectExceptionMessage('Public upload is only possible for publicly shared folders');
2235
-
2236
-		$storage = $this->createMock(IStorage::class);
2237
-		$storage->method('instanceOfStorage')
2238
-			->willReturnMap([
2239
-				['OCA\Files_Sharing\External\Storage', false],
2240
-				['OCA\Files_Sharing\SharedStorage', false],
2241
-			]);
2242
-
2243
-		$file = $this->createMock(File::class);
2244
-		$file->method('getId')->willReturn(42);
2245
-		$file->method('getStorage')->willReturn($storage);
2246
-
2247
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2248
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($file);
2249
-		$this->rootFolder->method('getById')
2250
-			->willReturn([]);
2251
-
2252
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2253
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2254
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2255
-
2256
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true');
2257
-	}
2258
-
2259
-	public function testCreateShareLinkPublicUploadFolder(): void {
2260
-		$ocs = $this->mockFormatShare();
2261
-
2262
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2263
-		$path->method('getId')->willReturn(1);
2264
-		$storage = $this->createMock(IStorage::class);
2265
-		$storage->method('instanceOfStorage')
2266
-			->willReturnMap([
2267
-				['OCA\Files_Sharing\External\Storage', false],
2268
-				['OCA\Files_Sharing\SharedStorage', false],
2269
-			]);
2270
-		$path->method('getStorage')->willReturn($storage);
2271
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2272
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2273
-		$this->rootFolder->method('getById')
2274
-			->willReturn([]);
2275
-
2276
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2277
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2278
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2279
-
2280
-		$this->shareManager->expects($this->once())->method('createShare')->with(
2281
-			$this->callback(function (IShare $share) use ($path) {
2282
-				return $share->getNode() === $path
2283
-					&& $share->getShareType() === IShare::TYPE_LINK
2284
-					&& $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
2285
-					&& $share->getSharedBy() === 'currentUser'
2286
-					&& $share->getPassword() === null
2287
-					&& $share->getExpirationDate() === null;
2288
-			})
2289
-		)->willReturnArgument(0);
2290
-
2291
-		$expected = new DataResponse([]);
2292
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true', '', null, '');
2293
-
2294
-		$this->assertInstanceOf(get_class($expected), $result);
2295
-		$this->assertEquals($expected->getData(), $result->getData());
2296
-	}
2297
-
2298
-	public function testCreateShareLinkPassword(): void {
2299
-		$ocs = $this->mockFormatShare();
2300
-
2301
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2302
-		$path->method('getId')->willReturn(42);
2303
-		$storage = $this->createMock(IStorage::class);
2304
-		$storage->method('instanceOfStorage')
2305
-			->willReturnMap([
2306
-				['OCA\Files_Sharing\External\Storage', false],
2307
-				['OCA\Files_Sharing\SharedStorage', false],
2308
-			]);
2309
-		$path->method('getStorage')->willReturn($storage);
2310
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2311
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2312
-		$this->rootFolder->method('getById')
2313
-			->willReturn([]);
2314
-
2315
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2316
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2317
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2318
-
2319
-		$this->shareManager->expects($this->once())->method('createShare')->with(
2320
-			$this->callback(function (IShare $share) use ($path) {
2321
-				return $share->getNode() === $path
2322
-				&& $share->getShareType() === IShare::TYPE_LINK
2323
-				&& $share->getPermissions() === Constants::PERMISSION_READ // publicUpload was set to false
2324
-				&& $share->getSharedBy() === 'currentUser'
2325
-				&& $share->getPassword() === 'password'
2326
-				&& $share->getExpirationDate() === null;
2327
-			})
2328
-		)->willReturnArgument(0);
2329
-
2330
-		$expected = new DataResponse([]);
2331
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_READ, IShare::TYPE_LINK, null, 'false', 'password', null, '');
2332
-
2333
-		$this->assertInstanceOf(get_class($expected), $result);
2334
-		$this->assertEquals($expected->getData(), $result->getData());
2335
-	}
2336
-
2337
-	public function testCreateShareLinkSendPasswordByTalk(): void {
2338
-		$ocs = $this->mockFormatShare();
2339
-
2340
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2341
-		$path->method('getId')->willReturn(42);
2342
-		$storage = $this->createMock(IStorage::class);
2343
-		$storage->method('instanceOfStorage')
2344
-			->willReturnMap([
2345
-				['OCA\Files_Sharing\External\Storage', false],
2346
-				['OCA\Files_Sharing\SharedStorage', false],
2347
-			]);
2348
-		$path->method('getStorage')->willReturn($storage);
2349
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2350
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2351
-		$this->rootFolder->method('getById')
2352
-			->willReturn([]);
2353
-
2354
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2355
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2356
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2357
-
2358
-		$this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(true);
2359
-
2360
-		$this->shareManager->expects($this->once())->method('createShare')->with(
2361
-			$this->callback(function (IShare $share) use ($path) {
2362
-				return $share->getNode() === $path
2363
-				&& $share->getShareType() === IShare::TYPE_LINK
2364
-				&& $share->getPermissions() === (Constants::PERMISSION_ALL & ~(Constants::PERMISSION_SHARE))
2365
-				&& $share->getSharedBy() === 'currentUser'
2366
-				&& $share->getPassword() === 'password'
2367
-				&& $share->getSendPasswordByTalk() === true
2368
-				&& $share->getExpirationDate() === null;
2369
-			})
2370
-		)->willReturnArgument(0);
2371
-
2372
-		$expected = new DataResponse([]);
2373
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true', 'password', 'true', '');
2374
-
2375
-		$this->assertInstanceOf(get_class($expected), $result);
2376
-		$this->assertEquals($expected->getData(), $result->getData());
2377
-	}
2378
-
2379
-
2380
-	public function testCreateShareLinkSendPasswordByTalkWithTalkDisabled(): void {
2381
-		$this->expectException(OCSForbiddenException::class);
2382
-		$this->expectExceptionMessage('Sharing valid-path sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled');
2383
-
2384
-		$ocs = $this->mockFormatShare();
2385
-
2386
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2387
-		$path->method('getId')->willReturn(42);
2388
-		$storage = $this->createMock(IStorage::class);
2389
-		$storage->method('instanceOfStorage')
2390
-			->willReturnMap([
2391
-				['OCA\Files_Sharing\External\Storage', false],
2392
-				['OCA\Files_Sharing\SharedStorage', false],
2393
-			]);
2394
-		$path->method('getStorage')->willReturn($storage);
2395
-		$path->method('getPath')->willReturn('valid-path');
2396
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2397
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2398
-		$this->rootFolder->method('getById')
2399
-			->willReturn([]);
2400
-
2401
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2402
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2403
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2404
-
2405
-		$this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(false);
2406
-
2407
-		$this->shareManager->expects($this->never())->method('createShare');
2408
-
2409
-		$ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', 'password', 'true', '');
2410
-	}
2411
-
2412
-	public function testCreateShareValidExpireDate(): void {
2413
-		$ocs = $this->mockFormatShare();
2414
-
2415
-		$this->request
2416
-			->method('getParam')
2417
-			->willReturnMap([
2418
-				['path', null, 'valid-path'],
2419
-				['shareType', '-1', IShare::TYPE_LINK],
2420
-				['publicUpload', null, 'false'],
2421
-				['expireDate', '', '2000-01-01'],
2422
-				['password', '', ''],
2423
-			]);
2424
-
2425
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2426
-		$path->method('getId')->willReturn(42);
2427
-		$storage = $this->createMock(IStorage::class);
2428
-		$storage->method('instanceOfStorage')
2429
-			->willReturnMap([
2430
-				['OCA\Files_Sharing\External\Storage', false],
2431
-				['OCA\Files_Sharing\SharedStorage', false],
2432
-			]);
2433
-		$path->method('getStorage')->willReturn($storage);
2434
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2435
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2436
-		$this->rootFolder->method('getById')
2437
-			->willReturn([]);
2438
-
2439
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2440
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2441
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2442
-
2443
-		$this->shareManager->expects($this->once())->method('createShare')->with(
2444
-			$this->callback(function (IShare $share) use ($path) {
2445
-				$date = new \DateTime('2000-01-01');
2446
-				$date->setTime(0, 0, 0);
2447
-
2448
-				return $share->getNode() === $path
2449
-				&& $share->getShareType() === IShare::TYPE_LINK
2450
-				&& $share->getPermissions() === Constants::PERMISSION_READ | Constants::PERMISSION_SHARE
2451
-				&& $share->getSharedBy() === 'currentUser'
2452
-				&& $share->getPassword() === null
2453
-				&& $share->getExpirationDate() == $date;
2454
-			})
2455
-		)->willReturnArgument(0);
2456
-
2457
-		$expected = new DataResponse([]);
2458
-		$result = $ocs->createShare('valid-path', null, IShare::TYPE_LINK, null, 'false', '', null, '2000-01-01');
2459
-
2460
-		$this->assertInstanceOf(get_class($expected), $result);
2461
-		$this->assertEquals($expected->getData(), $result->getData());
2462
-	}
2463
-
2464
-
2465
-	public function testCreateShareInvalidExpireDate(): void {
2466
-		$this->expectException(OCSNotFoundException::class);
2467
-		$this->expectExceptionMessage('Invalid date. Format must be YYYY-MM-DD');
2468
-
2469
-		$ocs = $this->mockFormatShare();
2470
-
2471
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2472
-		$path->method('getId')->willReturn(42);
2473
-		$storage = $this->createMock(IStorage::class);
2474
-		$storage->method('instanceOfStorage')
2475
-			->willReturnMap([
2476
-				['OCA\Files_Sharing\External\Storage', false],
2477
-				['OCA\Files_Sharing\SharedStorage', false],
2478
-			]);
2479
-		$path->method('getStorage')->willReturn($storage);
2480
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2481
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2482
-		$this->rootFolder->method('getById')
2483
-			->willReturn([]);
2484
-
2485
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2486
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2487
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2488
-
2489
-		$ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '', null, 'a1b2d3');
2490
-	}
2491
-
2492
-	public function testCreateShareRemote(): void {
2493
-		$share = $this->newShare();
2494
-		$this->shareManager->method('newShare')->willReturn($share);
2495
-
2496
-		/** @var ShareAPIController $ocs */
2497
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
2498
-			->setConstructorArgs([
2499
-				$this->appName,
2500
-				$this->request,
2501
-				$this->shareManager,
2502
-				$this->groupManager,
2503
-				$this->userManager,
2504
-				$this->rootFolder,
2505
-				$this->urlGenerator,
2506
-				$this->l,
2507
-				$this->config,
2508
-				$this->appConfig,
2509
-				$this->appManager,
2510
-				$this->serverContainer,
2511
-				$this->userStatusManager,
2512
-				$this->previewManager,
2513
-				$this->dateTimeZone,
2514
-				$this->logger,
2515
-				$this->factory,
2516
-				$this->mailer,
2517
-				$this->tagManager,
2518
-				$this->getEmailValidatorWithStrictEmailCheck(),
2519
-				$this->trustedServers,
2520
-				$this->currentUser,
2521
-			])->onlyMethods(['formatShare'])
2522
-			->getMock();
2523
-
2524
-		[$userFolder, $path] = $this->getNonSharedUserFile();
2525
-		$this->rootFolder->expects($this->exactly(2))
2526
-			->method('getUserFolder')
2527
-			->with('currentUser')
2528
-			->willReturn($userFolder);
2529
-
2530
-		$userFolder->expects($this->once())
2531
-			->method('get')
2532
-			->with('valid-path')
2533
-			->willReturn($path);
2534
-		$userFolder->method('getById')
2535
-			->willReturn([]);
2536
-
2537
-		$this->userManager->method('userExists')->with('validUser')->willReturn(true);
2538
-
2539
-		$path->expects($this->once())
2540
-			->method('lock')
2541
-			->with(ILockingProvider::LOCK_SHARED);
2542
-
2543
-		$this->shareManager->method('createShare')
2544
-			->with($this->callback(function (IShare $share) use ($path) {
2545
-				return $share->getNode() === $path
2546
-					&& $share->getPermissions() === (
2547
-						Constants::PERMISSION_ALL
2548
-						& ~Constants::PERMISSION_DELETE
2549
-						& ~Constants::PERMISSION_CREATE
2550
-					)
2551
-					&& $share->getShareType() === IShare::TYPE_REMOTE
2552
-					&& $share->getSharedWith() === '[email protected]'
2553
-					&& $share->getSharedBy() === 'currentUser';
2554
-			}))
2555
-			->willReturnArgument(0);
2556
-
2557
-		$this->shareManager->method('outgoingServer2ServerSharesAllowed')->willReturn(true);
2558
-
2559
-		$expected = new DataResponse([]);
2560
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_REMOTE, '[email protected]');
2561
-
2562
-		$this->assertInstanceOf(get_class($expected), $result);
2563
-		$this->assertEquals($expected->getData(), $result->getData());
2564
-	}
2565
-
2566
-	public function testCreateShareRemoteGroup(): void {
2567
-		$share = $this->newShare();
2568
-		$this->shareManager->method('newShare')->willReturn($share);
2569
-
2570
-		/** @var ShareAPIController $ocs */
2571
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
2572
-			->setConstructorArgs([
2573
-				$this->appName,
2574
-				$this->request,
2575
-				$this->shareManager,
2576
-				$this->groupManager,
2577
-				$this->userManager,
2578
-				$this->rootFolder,
2579
-				$this->urlGenerator,
2580
-				$this->l,
2581
-				$this->config,
2582
-				$this->appConfig,
2583
-				$this->appManager,
2584
-				$this->serverContainer,
2585
-				$this->userStatusManager,
2586
-				$this->previewManager,
2587
-				$this->dateTimeZone,
2588
-				$this->logger,
2589
-				$this->factory,
2590
-				$this->mailer,
2591
-				$this->tagManager,
2592
-				$this->getEmailValidatorWithStrictEmailCheck(),
2593
-				$this->trustedServers,
2594
-				$this->currentUser,
2595
-			])->onlyMethods(['formatShare'])
2596
-			->getMock();
2597
-
2598
-		[$userFolder, $path] = $this->getNonSharedUserFile();
2599
-		$this->rootFolder->expects($this->exactly(2))
2600
-			->method('getUserFolder')
2601
-			->with('currentUser')
2602
-			->willReturn($userFolder);
2603
-
2604
-		$userFolder->expects($this->once())
2605
-			->method('get')
2606
-			->with('valid-path')
2607
-			->willReturn($path);
2608
-		$userFolder->method('getById')
2609
-			->willReturn([]);
2610
-
2611
-		$this->userManager->method('userExists')->with('validUser')->willReturn(true);
2612
-
2613
-		$path->expects($this->once())
2614
-			->method('lock')
2615
-			->with(ILockingProvider::LOCK_SHARED);
2616
-
2617
-		$this->shareManager->method('createShare')
2618
-			->with($this->callback(function (IShare $share) use ($path) {
2619
-				return $share->getNode() === $path
2620
-					&& $share->getPermissions() === (
2621
-						Constants::PERMISSION_ALL
2622
-						& ~Constants::PERMISSION_DELETE
2623
-						& ~Constants::PERMISSION_CREATE
2624
-					)
2625
-					&& $share->getShareType() === IShare::TYPE_REMOTE_GROUP
2626
-					&& $share->getSharedWith() === '[email protected]'
2627
-					&& $share->getSharedBy() === 'currentUser';
2628
-			}))
2629
-			->willReturnArgument(0);
2630
-
2631
-		$this->shareManager->method('outgoingServer2ServerGroupSharesAllowed')->willReturn(true);
2632
-
2633
-		$expected = new DataResponse([]);
2634
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_REMOTE_GROUP, '[email protected]');
2635
-
2636
-		$this->assertInstanceOf(get_class($expected), $result);
2637
-		$this->assertEquals($expected->getData(), $result->getData());
2638
-	}
2639
-
2640
-	public function testCreateShareRoom(): void {
2641
-		$ocs = $this->mockFormatShare();
2642
-
2643
-		$share = $this->newShare();
2644
-		$this->shareManager->method('newShare')->willReturn($share);
2645
-
2646
-		[$userFolder, $path] = $this->getNonSharedUserFile();
2647
-		$this->rootFolder->expects($this->exactly(2))
2648
-			->method('getUserFolder')
2649
-			->with('currentUser')
2650
-			->willReturn($userFolder);
2651
-
2652
-		$userFolder->expects($this->once())
2653
-			->method('get')
2654
-			->with('valid-path')
2655
-			->willReturn($path);
2656
-		$userFolder->method('getById')
2657
-			->willReturn([]);
2658
-
2659
-		$path->expects($this->once())
2660
-			->method('lock')
2661
-			->with(ILockingProvider::LOCK_SHARED);
2662
-
2663
-		$this->appManager->method('isEnabledForUser')
2664
-			->with('spreed')
2665
-			->willReturn(true);
2666
-
2667
-		// This is not possible anymore with PHPUnit 10+
2668
-		// as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
2669
-		// $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
2670
-		$helper = $this->getMockBuilder(\stdClass::class)
2671
-			->addMethods(['createShare'])
2672
-			->getMock();
2673
-		$helper->method('createShare')
2674
-			->with(
2675
-				$share,
2676
-				'recipientRoom',
2677
-				Constants::PERMISSION_ALL
2678
-				& ~Constants::PERMISSION_DELETE
2679
-				& ~Constants::PERMISSION_CREATE,
2680
-				''
2681
-			)->willReturnCallback(
2682
-				function ($share): void {
2683
-					$share->setSharedWith('recipientRoom');
2684
-					$share->setPermissions(Constants::PERMISSION_ALL);
2685
-				}
2686
-			);
2687
-
2688
-		$this->serverContainer->method('get')
2689
-			->with('\OCA\Talk\Share\Helper\ShareAPIController')
2690
-			->willReturn($helper);
2691
-
2692
-		$this->shareManager->method('createShare')
2693
-			->with($this->callback(function (IShare $share) use ($path) {
2694
-				return $share->getNode() === $path
2695
-					&& $share->getPermissions() === Constants::PERMISSION_ALL
2696
-					&& $share->getShareType() === IShare::TYPE_ROOM
2697
-					&& $share->getSharedWith() === 'recipientRoom'
2698
-					&& $share->getSharedBy() === 'currentUser';
2699
-			}))
2700
-			->willReturnArgument(0);
2701
-
2702
-		$expected = new DataResponse([]);
2703
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_ROOM, 'recipientRoom');
2704
-
2705
-		$this->assertInstanceOf(get_class($expected), $result);
2706
-		$this->assertEquals($expected->getData(), $result->getData());
2707
-	}
2708
-
2709
-
2710
-	public function testCreateShareRoomHelperNotAvailable(): void {
2711
-		$this->expectException(OCSForbiddenException::class);
2712
-		$this->expectExceptionMessage('Sharing valid-path failed because the back end does not support room shares');
2713
-
2714
-		$ocs = $this->mockFormatShare();
2715
-
2716
-		$share = $this->newShare();
2717
-		$this->shareManager->method('newShare')->willReturn($share);
2718
-
2719
-		[$userFolder, $path] = $this->getNonSharedUserFolder();
2720
-		$this->rootFolder->method('getUserFolder')
2721
-			->with('currentUser')
2722
-			->willReturn($userFolder);
2723
-
2724
-		$path->method('getPath')->willReturn('valid-path');
2725
-		$userFolder->expects($this->once())
2726
-			->method('get')
2727
-			->with('valid-path')
2728
-			->willReturn($path);
2729
-		$userFolder->method('getById')
2730
-			->willReturn([]);
2731
-
2732
-		$path->expects($this->once())
2733
-			->method('lock')
2734
-			->with(ILockingProvider::LOCK_SHARED);
2735
-
2736
-		$this->appManager->method('isEnabledForUser')
2737
-			->with('spreed')
2738
-			->willReturn(false);
2739
-
2740
-		$this->shareManager->expects($this->never())->method('createShare');
2741
-
2742
-		$ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_ROOM, 'recipientRoom');
2743
-	}
2744
-
2745
-
2746
-	public function testCreateShareRoomHelperThrowException(): void {
2747
-		$this->expectException(OCSNotFoundException::class);
2748
-		$this->expectExceptionMessage('Exception thrown by the helper');
2749
-
2750
-		$ocs = $this->mockFormatShare();
2751
-
2752
-		$share = $this->newShare();
2753
-		$share->setSharedBy('currentUser');
2754
-		$this->shareManager->method('newShare')->willReturn($share);
2755
-
2756
-		[$userFolder, $path] = $this->getNonSharedUserFile();
2757
-		$this->rootFolder->method('getUserFolder')
2758
-			->with('currentUser')
2759
-			->willReturn($userFolder);
2760
-
2761
-		$userFolder->expects($this->once())
2762
-			->method('get')
2763
-			->with('valid-path')
2764
-			->willReturn($path);
2765
-		$userFolder->method('getById')
2766
-			->willReturn([]);
2767
-
2768
-		$path->expects($this->once())
2769
-			->method('lock')
2770
-			->with(ILockingProvider::LOCK_SHARED);
2771
-
2772
-		$this->appManager->method('isEnabledForUser')
2773
-			->with('spreed')
2774
-			->willReturn(true);
2775
-
2776
-		// This is not possible anymore with PHPUnit 10+
2777
-		// as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
2778
-		// $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
2779
-		$helper = $this->getMockBuilder(\stdClass::class)
2780
-			->addMethods(['createShare'])
2781
-			->getMock();
2782
-		$helper->method('createShare')
2783
-			->with(
2784
-				$share,
2785
-				'recipientRoom',
2786
-				Constants::PERMISSION_ALL & ~(Constants::PERMISSION_CREATE | Constants::PERMISSION_DELETE),
2787
-				''
2788
-			)->willReturnCallback(
2789
-				function ($share): void {
2790
-					throw new OCSNotFoundException('Exception thrown by the helper');
2791
-				}
2792
-			);
2793
-
2794
-		$this->serverContainer->method('get')
2795
-			->with('\OCA\Talk\Share\Helper\ShareAPIController')
2796
-			->willReturn($helper);
2797
-
2798
-		$this->shareManager->expects($this->never())->method('createShare');
2799
-
2800
-		$ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_ROOM, 'recipientRoom');
2801
-	}
2802
-
2803
-	/**
2804
-	 * Test for https://github.com/owncloud/core/issues/22587
2805
-	 * TODO: Remove once proper solution is in place
2806
-	 */
2807
-	public function testCreateReshareOfFederatedMountNoDeletePermissions(): void {
2808
-		$share = Server::get(IManager::class)->newShare();
2809
-		$this->shareManager->method('newShare')->willReturn($share);
2810
-
2811
-		/** @var ShareAPIController&MockObject $ocs */
2812
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
2813
-			->setConstructorArgs([
2814
-				$this->appName,
2815
-				$this->request,
2816
-				$this->shareManager,
2817
-				$this->groupManager,
2818
-				$this->userManager,
2819
-				$this->rootFolder,
2820
-				$this->urlGenerator,
2821
-				$this->l,
2822
-				$this->config,
2823
-				$this->appConfig,
2824
-				$this->appManager,
2825
-				$this->serverContainer,
2826
-				$this->userStatusManager,
2827
-				$this->previewManager,
2828
-				$this->dateTimeZone,
2829
-				$this->logger,
2830
-				$this->factory,
2831
-				$this->mailer,
2832
-				$this->tagManager,
2833
-				$this->getEmailValidatorWithStrictEmailCheck(),
2834
-				$this->trustedServers,
2835
-				$this->currentUser,
2836
-			])->onlyMethods(['formatShare'])
2837
-			->getMock();
2838
-
2839
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
2840
-		$this->rootFolder->expects($this->exactly(2))
2841
-			->method('getUserFolder')
2842
-			->with('currentUser')
2843
-			->willReturn($userFolder);
2844
-
2845
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2846
-		$path->method('getId')->willReturn(42);
2847
-
2848
-		$storage = $this->createMock(IStorage::class);
2849
-		$storage->method('instanceOfStorage')
2850
-			->willReturnMap([
2851
-				['OCA\Files_Sharing\External\Storage', true],
2852
-				['OCA\Files_Sharing\SharedStorage', false],
2853
-			]);
2854
-		$userFolder->method('getStorage')->willReturn($storage);
2855
-		$path->method('getStorage')->willReturn($storage);
2856
-
2857
-		$path->method('getPermissions')->willReturn(Constants::PERMISSION_READ);
2858
-		$userFolder->expects($this->once())
2859
-			->method('get')
2860
-			->with('valid-path')
2861
-			->willReturn($path);
2862
-		$userFolder->method('getById')
2863
-			->willReturn([]);
2864
-
2865
-		$this->userManager->method('userExists')->with('validUser')->willReturn(true);
2866
-
2867
-		$this->shareManager
2868
-			->expects($this->once())
2869
-			->method('createShare')
2870
-			->with($this->callback(function (IShare $share) {
2871
-				return $share->getPermissions() === Constants::PERMISSION_READ;
2872
-			}))
2873
-			->willReturnArgument(0);
2874
-
2875
-		$ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER, 'validUser');
2876
-	}
2877
-
2878
-
2879
-	public function testUpdateShareCantAccess(): void {
2880
-		$this->expectException(OCSNotFoundException::class);
2881
-		$this->expectExceptionMessage('Wrong share ID, share does not exist');
2882
-
2883
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
2884
-		$share = $this->newShare();
2885
-		$share->setNode($node);
2886
-
2887
-		$node->expects($this->once())
2888
-			->method('lock')
2889
-			->with(ILockingProvider::LOCK_SHARED);
2890
-
2891
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2892
-
2893
-		$this->rootFolder->method('getUserFolder')
2894
-			->with($this->currentUser)
2895
-			->willReturn($userFolder);
2896
-
2897
-		$userFolder->method('getById')
2898
-			->with($share->getNodeId())
2899
-			->willReturn([$share->getNode()]);
2900
-
2901
-		$this->ocs->updateShare(42);
2902
-	}
2903
-
2904
-
2905
-	public function testUpdateNoParametersLink(): void {
2906
-		$this->expectException(OCSBadRequestException::class);
2907
-		$this->expectExceptionMessage('Wrong or no update parameter given');
2908
-
2909
-		$node = $this->getMockBuilder(Folder::class)->getMock();
2910
-		$share = $this->newShare();
2911
-		$share->setPermissions(Constants::PERMISSION_ALL)
2912
-			->setSharedBy($this->currentUser)
2913
-			->setShareType(IShare::TYPE_LINK)
2914
-			->setNode($node);
2915
-
2916
-		$node->expects($this->once())
2917
-			->method('lock')
2918
-			->with(ILockingProvider::LOCK_SHARED);
2919
-
2920
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2921
-
2922
-		$this->ocs->updateShare(42);
2923
-	}
2924
-
2925
-
2926
-	public function testUpdateNoParametersOther(): void {
2927
-		$this->expectException(OCSBadRequestException::class);
2928
-		$this->expectExceptionMessage('Wrong or no update parameter given');
2929
-
2930
-		$node = $this->getMockBuilder(Folder::class)->getMock();
2931
-		$share = $this->newShare();
2932
-		$share->setPermissions(Constants::PERMISSION_ALL)
2933
-			->setSharedBy($this->currentUser)
2934
-			->setShareType(IShare::TYPE_GROUP)
2935
-			->setNode($node);
2936
-
2937
-		$node->expects($this->once())
2938
-			->method('lock')
2939
-			->with(ILockingProvider::LOCK_SHARED);
2940
-
2941
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2942
-
2943
-		$this->ocs->updateShare(42);
2944
-	}
2945
-
2946
-	public function testUpdateLinkShareClear(): void {
2947
-		$ocs = $this->mockFormatShare();
2948
-
2949
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
2950
-		$node->method('getId')
2951
-			->willReturn(42);
2952
-		$share = $this->newShare();
2953
-		$share->setPermissions(Constants::PERMISSION_ALL)
2954
-			->setSharedBy($this->currentUser)
2955
-			->setShareType(IShare::TYPE_LINK)
2956
-			->setPassword('password')
2957
-			->setExpirationDate(new \DateTime())
2958
-			->setNote('note')
2959
-			->setLabel('label')
2960
-			->setHideDownload(true)
2961
-			->setPermissions(Constants::PERMISSION_ALL)
2962
-			->setNode($node);
2963
-
2964
-		$node->expects($this->once())
2965
-			->method('lock')
2966
-			->with(ILockingProvider::LOCK_SHARED);
2967
-
2968
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2969
-
2970
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
2971
-			$this->callback(function (IShare $share) {
2972
-				return $share->getPermissions() === Constants::PERMISSION_READ
2973
-				&& $share->getPassword() === null
2974
-				&& $share->getExpirationDate() === null
2975
-				// Once set a note or a label are never back to null, only to an
2976
-				// empty string.
2977
-				&& $share->getNote() === ''
2978
-				&& $share->getLabel() === ''
2979
-				&& $share->getHideDownload() === false;
2980
-			})
2981
-		)->willReturnArgument(0);
2982
-
2983
-		$this->shareManager->method('getSharedWith')
2984
-			->willReturn([]);
2985
-
2986
-		$this->rootFolder->method('getUserFolder')
2987
-			->with($this->currentUser)
2988
-			->willReturn($userFolder);
2989
-
2990
-		$userFolder->method('getById')
2991
-			->with(42)
2992
-			->willReturn([$node]);
2993
-		$userFolder->method('getFirstNodeById')
2994
-			->with(42)
2995
-			->willReturn($node);
2996
-
2997
-		$mountPoint = $this->createMock(IMountPoint::class);
2998
-		$node->method('getMountPoint')
2999
-			->willReturn($mountPoint);
3000
-		$mountPoint->method('getStorageRootId')
3001
-			->willReturn(42);
3002
-
3003
-		$expected = new DataResponse([]);
3004
-		$result = $ocs->updateShare(42, null, '', null, 'false', '', '', '', 'false');
3005
-
3006
-		$this->assertInstanceOf(get_class($expected), $result);
3007
-		$this->assertEquals($expected->getData(), $result->getData());
3008
-	}
3009
-
3010
-	public function testUpdateLinkShareSet(): void {
3011
-		$ocs = $this->mockFormatShare();
3012
-
3013
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3014
-		$folder->method('getId')
3015
-			->willReturn(42);
3016
-
3017
-		$share = Server::get(IManager::class)->newShare();
3018
-		$share->setPermissions(Constants::PERMISSION_ALL)
3019
-			->setSharedBy($this->currentUser)
3020
-			->setShareType(IShare::TYPE_LINK)
3021
-			->setNode($folder);
3022
-
3023
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3024
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3025
-
3026
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3027
-			$this->callback(function (IShare $share) {
3028
-				$date = new \DateTime('2000-01-01');
3029
-				$date->setTime(0, 0, 0);
3030
-
3031
-				return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3032
-				&& $share->getPassword() === 'password'
3033
-				&& $share->getExpirationDate() == $date
3034
-				&& $share->getNote() === 'note'
3035
-				&& $share->getLabel() === 'label'
3036
-				&& $share->getHideDownload() === true;
3037
-			})
3038
-		)->willReturnArgument(0);
3039
-
3040
-		$this->shareManager->method('getSharedWith')
3041
-			->willReturn([]);
3042
-
3043
-		$this->rootFolder->method('getUserFolder')
3044
-			->with($this->currentUser)
3045
-			->willReturn($userFolder);
3046
-
3047
-		$userFolder->method('getById')
3048
-			->with(42)
3049
-			->willReturn([$folder]);
3050
-
3051
-		$mountPoint = $this->createMock(IMountPoint::class);
3052
-		$folder->method('getMountPoint')
3053
-			->willReturn($mountPoint);
3054
-		$mountPoint->method('getStorageRootId')
3055
-			->willReturn(42);
3056
-
3057
-		$expected = new DataResponse([]);
3058
-		$result = $ocs->updateShare(42, null, 'password', null, 'true', '2000-01-01', 'note', 'label', 'true');
3059
-
3060
-		$this->assertInstanceOf(get_class($expected), $result);
3061
-		$this->assertEquals($expected->getData(), $result->getData());
3062
-	}
3063
-
3064
-	#[DataProvider('publicUploadParamsProvider')]
3065
-	public function testUpdateLinkShareEnablePublicUpload($permissions, $publicUpload, $expireDate, $password): void {
3066
-		$ocs = $this->mockFormatShare();
3067
-
3068
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3069
-		$folder->method('getId')
3070
-			->willReturn(42);
3071
-
3072
-		$share = Server::get(IManager::class)->newShare();
3073
-		$share->setPermissions(Constants::PERMISSION_ALL)
3074
-			->setSharedBy($this->currentUser)
3075
-			->setShareType(IShare::TYPE_LINK)
3076
-			->setPassword('password')
3077
-			->setNode($folder);
3078
-
3079
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3080
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3081
-		$this->shareManager->method('getSharedWith')->willReturn([]);
3082
-
3083
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3084
-			$this->callback(function (IShare $share) {
3085
-				return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3086
-				&& $share->getPassword() === 'password'
3087
-				&& $share->getExpirationDate() === null;
3088
-			})
3089
-		)->willReturnArgument(0);
3090
-
3091
-		$this->rootFolder->method('getUserFolder')
3092
-			->with($this->currentUser)
3093
-			->willReturn($userFolder);
3094
-
3095
-		$userFolder->method('getById')
3096
-			->with(42)
3097
-			->willReturn([$folder]);
3098
-
3099
-		$mountPoint = $this->createMock(IMountPoint::class);
3100
-		$folder->method('getMountPoint')
3101
-			->willReturn($mountPoint);
3102
-		$mountPoint->method('getStorageRootId')
3103
-			->willReturn(42);
3104
-
3105
-		$expected = new DataResponse([]);
3106
-		$result = $ocs->updateShare(42, $permissions, $password, null, $publicUpload, $expireDate);
3107
-
3108
-		$this->assertInstanceOf(get_class($expected), $result);
3109
-		$this->assertEquals($expected->getData(), $result->getData());
3110
-	}
3111
-
3112
-
3113
-	public static function publicLinkValidPermissionsProvider() {
3114
-		return [
3115
-			[Constants::PERMISSION_CREATE],
3116
-			[Constants::PERMISSION_READ],
3117
-			[Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE],
3118
-			[Constants::PERMISSION_READ | Constants::PERMISSION_DELETE],
3119
-			[Constants::PERMISSION_READ | Constants::PERMISSION_CREATE],
3120
-		];
3121
-	}
3122
-
3123
-	#[DataProvider('publicLinkValidPermissionsProvider')]
3124
-	public function testUpdateLinkShareSetCRUDPermissions($permissions): void {
3125
-		$ocs = $this->mockFormatShare();
3126
-
3127
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3128
-		$folder->method('getId')
3129
-			->willReturn(42);
3130
-
3131
-		$share = Server::get(IManager::class)->newShare();
3132
-		$share->setPermissions(Constants::PERMISSION_ALL)
3133
-			->setSharedBy($this->currentUser)
3134
-			->setShareType(IShare::TYPE_LINK)
3135
-			->setPassword('password')
3136
-			->setNode($folder);
3137
-
3138
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3139
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3140
-		$this->shareManager->method('getSharedWith')->willReturn([]);
3141
-
3142
-		$this->shareManager
3143
-			->expects($this->any())
3144
-			->method('updateShare')
3145
-			->willReturnArgument(0);
3146
-
3147
-		$this->rootFolder->method('getUserFolder')
3148
-			->with($this->currentUser)
3149
-			->willReturn($userFolder);
3150
-
3151
-		$userFolder->method('getById')
3152
-			->with(42)
3153
-			->willReturn([$folder]);
3154
-
3155
-		$mountPoint = $this->createMock(IMountPoint::class);
3156
-		$folder->method('getMountPoint')
3157
-			->willReturn($mountPoint);
3158
-		$mountPoint->method('getStorageRootId')
3159
-			->willReturn(42);
3160
-
3161
-		$expected = new DataResponse([]);
3162
-		$result = $ocs->updateShare(42, $permissions, 'password', null, null, null);
3163
-
3164
-		$this->assertInstanceOf(get_class($expected), $result);
3165
-		$this->assertEquals($expected->getData(), $result->getData());
3166
-	}
3167
-
3168
-	public static function publicLinkInvalidPermissionsProvider1() {
3169
-		return [
3170
-			[Constants::PERMISSION_DELETE],
3171
-			[Constants::PERMISSION_UPDATE],
3172
-			[Constants::PERMISSION_SHARE],
3173
-		];
3174
-	}
3175
-
3176
-	#[DataProvider('publicLinkInvalidPermissionsProvider1')]
3177
-	public function testUpdateLinkShareSetInvalidCRUDPermissions1($permissions): void {
3178
-		$this->expectException(OCSBadRequestException::class);
3179
-		$this->expectExceptionMessage('Share must at least have READ or CREATE permissions');
3180
-
3181
-		$this->testUpdateLinkShareSetCRUDPermissions($permissions, null);
3182
-	}
3183
-
3184
-	public static function publicLinkInvalidPermissionsProvider2() {
3185
-		return [
3186
-			[Constants::PERMISSION_CREATE | Constants::PERMISSION_DELETE],
3187
-			[Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE],
3188
-		];
3189
-	}
3190
-
3191
-	#[DataProvider('publicLinkInvalidPermissionsProvider2')]
3192
-	public function testUpdateLinkShareSetInvalidCRUDPermissions2($permissions): void {
3193
-		$this->expectException(OCSBadRequestException::class);
3194
-		$this->expectExceptionMessage('Share must have READ permission if UPDATE or DELETE permission is set');
3195
-
3196
-		$this->testUpdateLinkShareSetCRUDPermissions($permissions);
3197
-	}
3198
-
3199
-	public function testUpdateLinkShareInvalidDate(): void {
3200
-		$this->expectException(OCSBadRequestException::class);
3201
-		$this->expectExceptionMessage('Invalid date. Format must be YYYY-MM-DD');
3202
-
3203
-		$ocs = $this->mockFormatShare();
3204
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3205
-		$userFolder->method('getById')
3206
-			->with(42)
3207
-			->willReturn([$folder]);
3208
-		$this->rootFolder->method('getUserFolder')
3209
-			->with($this->currentUser)
3210
-			->willReturn($userFolder);
3211
-
3212
-		$folder->method('getId')
3213
-			->willReturn(42);
3214
-
3215
-		$share = Server::get(IManager::class)->newShare();
3216
-		$share->setPermissions(Constants::PERMISSION_ALL)
3217
-			->setSharedBy($this->currentUser)
3218
-			->setShareType(IShare::TYPE_LINK)
3219
-			->setNode($folder);
3220
-
3221
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3222
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3223
-
3224
-		$ocs->updateShare(42, null, 'password', null, 'true', '2000-01-a');
3225
-	}
3226
-
3227
-	public static function publicUploadParamsProvider() {
3228
-		return [
3229
-			[null, 'true', null, 'password'],
3230
-			// legacy had no delete
3231
-			[
3232
-				Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE,
3233
-				'true', null, 'password'
3234
-			],
3235
-			// correct
3236
-			[
3237
-				Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE,
3238
-				null, null, 'password'
3239
-			],
3240
-		];
3241
-	}
3242
-
3243
-	#[DataProvider('publicUploadParamsProvider')]
3244
-	public function testUpdateLinkSharePublicUploadNotAllowed($permissions, $publicUpload, $expireDate, $password): void {
3245
-		$this->expectException(OCSForbiddenException::class);
3246
-		$this->expectExceptionMessage('Public upload disabled by the administrator');
3247
-
3248
-		$ocs = $this->mockFormatShare();
3249
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3250
-		$userFolder->method('getById')
3251
-			->with(42)
3252
-			->willReturn([$folder]);
3253
-		$this->rootFolder->method('getUserFolder')
3254
-			->with($this->currentUser)
3255
-			->willReturn($userFolder);
3256
-
3257
-		$folder->method('getId')->willReturn(42);
3258
-
3259
-		$share = Server::get(IManager::class)->newShare();
3260
-		$share->setPermissions(Constants::PERMISSION_ALL)
3261
-			->setSharedBy($this->currentUser)
3262
-			->setShareType(IShare::TYPE_LINK)
3263
-			->setNode($folder);
3264
-
3265
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3266
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(false);
3267
-
3268
-		$ocs->updateShare(42, $permissions, $password, null, $publicUpload, $expireDate);
3269
-	}
3270
-
3271
-
3272
-	public function testUpdateLinkSharePublicUploadOnFile(): void {
3273
-		$this->expectException(OCSBadRequestException::class);
3274
-		$this->expectExceptionMessage('Public upload is only possible for publicly shared folders');
3275
-
3276
-		$ocs = $this->mockFormatShare();
3277
-
3278
-		$file = $this->getMockBuilder(File::class)->getMock();
3279
-		$file->method('getId')
3280
-			->willReturn(42);
3281
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3282
-		$userFolder->method('getById')
3283
-			->with(42)
3284
-			->willReturn([$folder]);
3285
-		$this->rootFolder->method('getUserFolder')
3286
-			->with($this->currentUser)
3287
-			->willReturn($userFolder);
3288
-
3289
-		$share = Server::get(IManager::class)->newShare();
3290
-		$share->setPermissions(Constants::PERMISSION_ALL)
3291
-			->setSharedBy($this->currentUser)
3292
-			->setShareType(IShare::TYPE_LINK)
3293
-			->setNode($file);
3294
-
3295
-		$this->shareManager
3296
-			->method('getShareById')
3297
-			->with('ocinternal:42')
3298
-			->willReturn($share);
3299
-		$this->shareManager
3300
-			->method('shareApiLinkAllowPublicUpload')
3301
-			->willReturn(true);
3302
-		$this->shareManager
3303
-			->method('updateShare')
3304
-			->with($share)
3305
-			->willThrowException(new \InvalidArgumentException('File shares cannot have create or delete permissions'));
3306
-
3307
-		$ocs->updateShare(42, null, 'password', null, 'true', '');
3308
-	}
3309
-
3310
-	public function testUpdateLinkSharePasswordDoesNotChangeOther(): void {
3311
-		$ocs = $this->mockFormatShare();
3312
-
3313
-		$date = new \DateTime('2000-01-01');
3314
-		$date->setTime(0, 0, 0);
3315
-
3316
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
3317
-		$node->method('getId')->willReturn(42);
3318
-		$userFolder->method('getById')
3319
-			->with(42)
3320
-			->willReturn([$node]);
3321
-		$this->rootFolder->method('getUserFolder')
3322
-			->with($this->currentUser)
3323
-			->willReturn($userFolder);
3324
-		$share = $this->newShare();
3325
-		$share->setPermissions(Constants::PERMISSION_ALL)
3326
-			->setSharedBy($this->currentUser)
3327
-			->setShareType(IShare::TYPE_LINK)
3328
-			->setPassword('password')
3329
-			->setSendPasswordByTalk(true)
3330
-			->setExpirationDate($date)
3331
-			->setNote('note')
3332
-			->setLabel('label')
3333
-			->setHideDownload(true)
3334
-			->setPermissions(Constants::PERMISSION_ALL)
3335
-			->setNode($node);
3336
-
3337
-		$node->expects($this->once())
3338
-			->method('lock')
3339
-			->with(ILockingProvider::LOCK_SHARED);
3340
-
3341
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3342
-
3343
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3344
-			$this->callback(function (IShare $share) use ($date) {
3345
-				return $share->getPermissions() === Constants::PERMISSION_ALL
3346
-				&& $share->getPassword() === 'newpassword'
3347
-				&& $share->getSendPasswordByTalk() === true
3348
-				&& $share->getExpirationDate() === $date
3349
-				&& $share->getNote() === 'note'
3350
-				&& $share->getLabel() === 'label'
3351
-				&& $share->getHideDownload() === true;
3352
-			})
3353
-		)->willReturnArgument(0);
3354
-
3355
-		$expected = new DataResponse([]);
3356
-		$result = $ocs->updateShare(42, null, 'newpassword', null, null, null, null, null, null);
3357
-
3358
-		$this->assertInstanceOf(get_class($expected), $result);
3359
-		$this->assertEquals($expected->getData(), $result->getData());
3360
-	}
3361
-
3362
-	public function testUpdateLinkShareSendPasswordByTalkDoesNotChangeOther(): void {
3363
-		$ocs = $this->mockFormatShare();
3364
-
3365
-		$date = new \DateTime('2000-01-01');
3366
-		$date->setTime(0, 0, 0);
3367
-
3368
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
3369
-		$userFolder->method('getById')
3370
-			->with(42)
3371
-			->willReturn([$node]);
3372
-		$this->rootFolder->method('getUserFolder')
3373
-			->with($this->currentUser)
3374
-			->willReturn($userFolder);
3375
-		$node->method('getId')->willReturn(42);
3376
-		$share = $this->newShare();
3377
-		$share->setPermissions(Constants::PERMISSION_ALL)
3378
-			->setSharedBy($this->currentUser)
3379
-			->setShareType(IShare::TYPE_LINK)
3380
-			->setPassword('password')
3381
-			->setSendPasswordByTalk(false)
3382
-			->setExpirationDate($date)
3383
-			->setNote('note')
3384
-			->setLabel('label')
3385
-			->setHideDownload(true)
3386
-			->setPermissions(Constants::PERMISSION_ALL)
3387
-			->setNode($node);
3388
-
3389
-		$node->expects($this->once())
3390
-			->method('lock')
3391
-			->with(ILockingProvider::LOCK_SHARED);
3392
-
3393
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3394
-
3395
-		$this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(true);
3396
-
3397
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3398
-			$this->callback(function (IShare $share) use ($date) {
3399
-				return $share->getPermissions() === Constants::PERMISSION_ALL
3400
-				&& $share->getPassword() === 'password'
3401
-				&& $share->getSendPasswordByTalk() === true
3402
-				&& $share->getExpirationDate() === $date
3403
-				&& $share->getNote() === 'note'
3404
-				&& $share->getLabel() === 'label'
3405
-				&& $share->getHideDownload() === true;
3406
-			})
3407
-		)->willReturnArgument(0);
3408
-
3409
-		$expected = new DataResponse([]);
3410
-		$result = $ocs->updateShare(42, null, null, 'true', null, null, null, null, null);
3411
-
3412
-		$this->assertInstanceOf(get_class($expected), $result);
3413
-		$this->assertEquals($expected->getData(), $result->getData());
3414
-	}
3415
-
3416
-
3417
-	public function testUpdateLinkShareSendPasswordByTalkWithTalkDisabledDoesNotChangeOther(): void {
3418
-		$this->expectException(OCSForbiddenException::class);
3419
-		$this->expectExceptionMessage('"Sending the password by Nextcloud Talk" for sharing a file or folder failed because Nextcloud Talk is not enabled.');
3420
-
3421
-		$ocs = $this->mockFormatShare();
3422
-
3423
-		$date = new \DateTime('2000-01-01');
3424
-		$date->setTime(0, 0, 0);
3425
-
3426
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
3427
-		$userFolder->method('getById')
3428
-			->with(42)
3429
-			->willReturn([$node]);
3430
-		$this->rootFolder->method('getUserFolder')
3431
-			->with($this->currentUser)
3432
-			->willReturn($userFolder);
3433
-		$node->method('getId')->willReturn(42);
3434
-		$share = $this->newShare();
3435
-		$share->setPermissions(Constants::PERMISSION_ALL)
3436
-			->setSharedBy($this->currentUser)
3437
-			->setShareType(IShare::TYPE_LINK)
3438
-			->setPassword('password')
3439
-			->setSendPasswordByTalk(false)
3440
-			->setExpirationDate($date)
3441
-			->setNote('note')
3442
-			->setLabel('label')
3443
-			->setHideDownload(true)
3444
-			->setPermissions(Constants::PERMISSION_ALL)
3445
-			->setNode($node);
3446
-
3447
-		$node->expects($this->once())
3448
-			->method('lock')
3449
-			->with(ILockingProvider::LOCK_SHARED);
3450
-
3451
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3452
-
3453
-		$this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(false);
3454
-
3455
-		$this->shareManager->expects($this->never())->method('updateShare');
3456
-
3457
-		$ocs->updateShare(42, null, null, 'true', null, null, null, null, null);
3458
-	}
3459
-
3460
-	public function testUpdateLinkShareDoNotSendPasswordByTalkDoesNotChangeOther(): void {
3461
-		$ocs = $this->mockFormatShare();
3462
-
3463
-		$date = new \DateTime('2000-01-01');
3464
-		$date->setTime(0, 0, 0);
3465
-
3466
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
3467
-		$userFolder->method('getById')
3468
-			->with(42)
3469
-			->willReturn([$node]);
3470
-		$this->rootFolder->method('getUserFolder')
3471
-			->with($this->currentUser)
3472
-			->willReturn($userFolder);
3473
-		$node->method('getId')->willReturn(42);
3474
-		$share = $this->newShare();
3475
-		$share->setPermissions(Constants::PERMISSION_ALL)
3476
-			->setSharedBy($this->currentUser)
3477
-			->setShareType(IShare::TYPE_LINK)
3478
-			->setPassword('password')
3479
-			->setSendPasswordByTalk(true)
3480
-			->setExpirationDate($date)
3481
-			->setNote('note')
3482
-			->setLabel('label')
3483
-			->setHideDownload(true)
3484
-			->setPermissions(Constants::PERMISSION_ALL)
3485
-			->setNode($node);
3486
-
3487
-		$node->expects($this->once())
3488
-			->method('lock')
3489
-			->with(ILockingProvider::LOCK_SHARED);
3490
-
3491
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3492
-
3493
-		$this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(true);
3494
-
3495
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3496
-			$this->callback(function (IShare $share) use ($date) {
3497
-				return $share->getPermissions() === Constants::PERMISSION_ALL
3498
-				&& $share->getPassword() === 'password'
3499
-				&& $share->getSendPasswordByTalk() === false
3500
-				&& $share->getExpirationDate() === $date
3501
-				&& $share->getNote() === 'note'
3502
-				&& $share->getLabel() === 'label'
3503
-				&& $share->getHideDownload() === true;
3504
-			})
3505
-		)->willReturnArgument(0);
3506
-
3507
-		$expected = new DataResponse([]);
3508
-		$result = $ocs->updateShare(42, null, null, 'false', null, null, null, null, null);
3509
-
3510
-		$this->assertInstanceOf(get_class($expected), $result);
3511
-		$this->assertEquals($expected->getData(), $result->getData());
3512
-	}
3513
-
3514
-	public function testUpdateLinkShareDoNotSendPasswordByTalkWithTalkDisabledDoesNotChangeOther(): void {
3515
-		$ocs = $this->mockFormatShare();
3516
-
3517
-		$date = new \DateTime('2000-01-01');
3518
-		$date->setTime(0, 0, 0);
3519
-
3520
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
3521
-		$node->method('getId')
3522
-			->willReturn(42);
3523
-
3524
-		$share = $this->newShare();
3525
-		$share->setPermissions(Constants::PERMISSION_ALL)
3526
-			->setSharedBy($this->currentUser)
3527
-			->setShareType(IShare::TYPE_LINK)
3528
-			->setPassword('password')
3529
-			->setSendPasswordByTalk(true)
3530
-			->setExpirationDate($date)
3531
-			->setNote('note')
3532
-			->setLabel('label')
3533
-			->setHideDownload(true)
3534
-			->setPermissions(Constants::PERMISSION_ALL)
3535
-			->setNode($node);
3536
-
3537
-		$node->expects($this->once())
3538
-			->method('lock')
3539
-			->with(ILockingProvider::LOCK_SHARED);
3540
-
3541
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3542
-
3543
-		$this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(false);
3544
-
3545
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3546
-			$this->callback(function (IShare $share) use ($date) {
3547
-				return $share->getPermissions() === Constants::PERMISSION_ALL
3548
-				&& $share->getPassword() === 'password'
3549
-				&& $share->getSendPasswordByTalk() === false
3550
-				&& $share->getExpirationDate() === $date
3551
-				&& $share->getNote() === 'note'
3552
-				&& $share->getLabel() === 'label'
3553
-				&& $share->getHideDownload() === true;
3554
-			})
3555
-		)->willReturnArgument(0);
3556
-
3557
-		$this->rootFolder->method('getUserFolder')
3558
-			->with($this->currentUser)
3559
-			->willReturn($userFolder);
3560
-
3561
-		$userFolder->method('getById')
3562
-			->with(42)
3563
-			->willReturn([$node]);
3564
-
3565
-		$mountPoint = $this->createMock(IMountPoint::class);
3566
-		$node->method('getMountPoint')
3567
-			->willReturn($mountPoint);
3568
-		$mountPoint->method('getStorageRootId')
3569
-			->willReturn(42);
3570
-
3571
-		$mountPoint = $this->createMock(IMountPoint::class);
3572
-		$node->method('getMountPoint')
3573
-			->willReturn($mountPoint);
3574
-		$mountPoint->method('getStorageRootId')
3575
-			->willReturn(42);
3576
-
3577
-		$expected = new DataResponse([]);
3578
-		$result = $ocs->updateShare(42, null, null, 'false', null, null, null, null, null);
3579
-
3580
-		$this->assertInstanceOf(get_class($expected), $result);
3581
-		$this->assertEquals($expected->getData(), $result->getData());
3582
-	}
3583
-
3584
-	public function testUpdateLinkShareExpireDateDoesNotChangeOther(): void {
3585
-		$ocs = $this->mockFormatShare();
3586
-
3587
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
3588
-		$node->method('getId')
3589
-			->willReturn(42);
3590
-
3591
-		$share = $this->newShare();
3592
-		$share->setPermissions(Constants::PERMISSION_ALL)
3593
-			->setSharedBy($this->currentUser)
3594
-			->setShareType(IShare::TYPE_LINK)
3595
-			->setPassword('password')
3596
-			->setSendPasswordByTalk(true)
3597
-			->setExpirationDate(new \DateTime())
3598
-			->setNote('note')
3599
-			->setLabel('label')
3600
-			->setHideDownload(true)
3601
-			->setPermissions(Constants::PERMISSION_ALL)
3602
-			->setNode($node);
3603
-
3604
-		$node->expects($this->once())
3605
-			->method('lock')
3606
-			->with(ILockingProvider::LOCK_SHARED);
3607
-
3608
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3609
-
3610
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3611
-			$this->callback(function (IShare $share) {
3612
-				$date = new \DateTime('2010-12-23');
3613
-				$date->setTime(0, 0, 0);
3614
-
3615
-				return $share->getPermissions() === Constants::PERMISSION_ALL
3616
-				&& $share->getPassword() === 'password'
3617
-				&& $share->getSendPasswordByTalk() === true
3618
-				&& $share->getExpirationDate() == $date
3619
-				&& $share->getNote() === 'note'
3620
-				&& $share->getLabel() === 'label'
3621
-				&& $share->getHideDownload() === true;
3622
-			})
3623
-		)->willReturnArgument(0);
3624
-
3625
-		$this->rootFolder->method('getUserFolder')
3626
-			->with($this->currentUser)
3627
-			->willReturn($userFolder);
3628
-
3629
-		$userFolder->method('getById')
3630
-			->with(42)
3631
-			->willReturn([$node]);
3632
-
3633
-		$mountPoint = $this->createMock(IMountPoint::class);
3634
-		$node->method('getMountPoint')
3635
-			->willReturn($mountPoint);
3636
-		$mountPoint->method('getStorageRootId')
3637
-			->willReturn(42);
3638
-
3639
-		$expected = new DataResponse([]);
3640
-		$result = $ocs->updateShare(42, null, null, null, null, '2010-12-23', null, null, null);
3641
-
3642
-		$this->assertInstanceOf(get_class($expected), $result);
3643
-		$this->assertEquals($expected->getData(), $result->getData());
3644
-	}
3645
-
3646
-	public function testUpdateLinkSharePublicUploadDoesNotChangeOther(): void {
3647
-		$ocs = $this->mockFormatShare();
3648
-
3649
-		$date = new \DateTime('2000-01-01');
3650
-
3651
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3652
-		$folder->method('getId')
3653
-			->willReturn(42);
3654
-
3655
-		$share = Server::get(IManager::class)->newShare();
3656
-		$share->setPermissions(Constants::PERMISSION_ALL)
3657
-			->setSharedBy($this->currentUser)
3658
-			->setShareType(IShare::TYPE_LINK)
3659
-			->setPassword('password')
3660
-			->setSendPasswordByTalk(true)
3661
-			->setExpirationDate($date)
3662
-			->setNote('note')
3663
-			->setLabel('label')
3664
-			->setHideDownload(true)
3665
-			->setPermissions(Constants::PERMISSION_ALL)
3666
-			->setNode($folder);
3667
-
3668
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3669
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3670
-
3671
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3672
-			$this->callback(function (IShare $share) use ($date) {
3673
-				return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3674
-				&& $share->getPassword() === 'password'
3675
-				&& $share->getSendPasswordByTalk() === true
3676
-				&& $share->getExpirationDate() === $date
3677
-				&& $share->getNote() === 'note'
3678
-				&& $share->getLabel() === 'label'
3679
-				&& $share->getHideDownload() === true;
3680
-			})
3681
-		)->willReturnArgument(0);
3682
-
3683
-		$this->shareManager->method('getSharedWith')
3684
-			->willReturn([]);
3685
-
3686
-		$this->rootFolder->method('getUserFolder')
3687
-			->with($this->currentUser)
3688
-			->willReturn($userFolder);
3689
-
3690
-		$userFolder->method('getById')
3691
-			->with(42)
3692
-			->willReturn([$folder]);
3693
-
3694
-		$mountPoint = $this->createMock(IMountPoint::class);
3695
-		$folder->method('getMountPoint')
3696
-			->willReturn($mountPoint);
3697
-		$mountPoint->method('getStorageRootId')
3698
-			->willReturn(42);
3699
-
3700
-		$expected = new DataResponse([]);
3701
-		$result = $ocs->updateShare(42, null, null, null, 'true', null, null, null, null);
3702
-
3703
-		$this->assertInstanceOf(get_class($expected), $result);
3704
-		$this->assertEquals($expected->getData(), $result->getData());
3705
-	}
3706
-
3707
-	public function testUpdateLinkSharePermissions(): void {
3708
-		$ocs = $this->mockFormatShare();
3709
-
3710
-		$date = new \DateTime('2000-01-01');
3711
-
3712
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3713
-		$folder->method('getId')
3714
-			->willReturn(42);
3715
-
3716
-		$share = Server::get(IManager::class)->newShare();
3717
-		$share->setPermissions(Constants::PERMISSION_ALL)
3718
-			->setSharedBy($this->currentUser)
3719
-			->setShareType(IShare::TYPE_LINK)
3720
-			->setPassword('password')
3721
-			->setSendPasswordByTalk(true)
3722
-			->setExpirationDate($date)
3723
-			->setNote('note')
3724
-			->setLabel('label')
3725
-			->setHideDownload(true)
3726
-			->setPermissions(Constants::PERMISSION_ALL)
3727
-			->setNode($folder);
3728
-
3729
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3730
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3731
-
3732
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3733
-			$this->callback(function (IShare $share) use ($date): bool {
3734
-				return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3735
-				&& $share->getPassword() === 'password'
3736
-				&& $share->getSendPasswordByTalk() === true
3737
-				&& $share->getExpirationDate() === $date
3738
-				&& $share->getNote() === 'note'
3739
-				&& $share->getLabel() === 'label'
3740
-				&& $share->getHideDownload() === true;
3741
-			})
3742
-		)->willReturnArgument(0);
3743
-
3744
-		$this->shareManager->method('getSharedWith')->willReturn([]);
3745
-
3746
-		$this->rootFolder->method('getUserFolder')
3747
-			->with($this->currentUser)
3748
-			->willReturn($userFolder);
3749
-
3750
-		$userFolder->method('getById')
3751
-			->with(42)
3752
-			->willReturn([$folder]);
3753
-
3754
-		$mountPoint = $this->createMock(IMountPoint::class);
3755
-		$folder->method('getMountPoint')
3756
-			->willReturn($mountPoint);
3757
-		$mountPoint->method('getStorageRootId')
3758
-			->willReturn(42);
3759
-
3760
-		$expected = new DataResponse([]);
3761
-		$result = $ocs->updateShare(42, 7, null, null, 'true', null, null, null, null);
3762
-
3763
-		$this->assertInstanceOf(get_class($expected), $result);
3764
-		$this->assertEquals($expected->getData(), $result->getData());
3765
-	}
3766
-
3767
-	public function testUpdateLinkSharePermissionsShare(): void {
3768
-		$ocs = $this->mockFormatShare();
3769
-
3770
-		$date = new \DateTime('2000-01-01');
3771
-
3772
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3773
-		$folder->method('getId')
3774
-			->willReturn(42);
3775
-
3776
-		$share = Server::get(IManager::class)->newShare();
3777
-		$share->setPermissions(Constants::PERMISSION_ALL)
3778
-			->setSharedBy($this->currentUser)
3779
-			->setShareType(IShare::TYPE_LINK)
3780
-			->setPassword('password')
3781
-			->setSendPasswordByTalk(true)
3782
-			->setExpirationDate($date)
3783
-			->setNote('note')
3784
-			->setLabel('label')
3785
-			->setHideDownload(true)
3786
-			->setPermissions(Constants::PERMISSION_READ)
3787
-			->setNode($folder);
3788
-
3789
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3790
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3791
-
3792
-		$this->shareManager->expects($this->once())
3793
-			->method('updateShare')
3794
-			->with(
3795
-				$this->callback(function (IShare $share) use ($date) {
3796
-					return $share->getPermissions() === Constants::PERMISSION_ALL
3797
-						&& $share->getPassword() === 'password'
3798
-						&& $share->getSendPasswordByTalk() === true
3799
-						&& $share->getExpirationDate() === $date
3800
-						&& $share->getNote() === 'note'
3801
-						&& $share->getLabel() === 'label'
3802
-						&& $share->getHideDownload() === true;
3803
-				})
3804
-			)->willReturnArgument(0);
3805
-
3806
-		$this->rootFolder->method('getUserFolder')
3807
-			->with($this->currentUser)
3808
-			->willReturn($userFolder);
3809
-
3810
-		$userFolder->method('getById')
3811
-			->with(42)
3812
-			->willReturn([$folder]);
3813
-
3814
-		$mountPoint = $this->createMock(IMountPoint::class);
3815
-		$folder->method('getMountPoint')
3816
-			->willReturn($mountPoint);
3817
-		$mountPoint->method('getStorageRootId')
3818
-			->willReturn(42);
3819
-
3820
-		$this->shareManager->method('getSharedWith')->willReturn([]);
3821
-
3822
-		$expected = new DataResponse([]);
3823
-		$result = $ocs->updateShare(42, Constants::PERMISSION_ALL, null, null, null, null, null, null, null);
3824
-
3825
-		$this->assertInstanceOf(get_class($expected), $result);
3826
-		$this->assertEquals($expected->getData(), $result->getData());
3827
-	}
3828
-
3829
-	public function testUpdateOtherPermissions(): void {
3830
-		$ocs = $this->mockFormatShare();
3831
-
3832
-		[$userFolder, $file] = $this->getNonSharedUserFolder();
3833
-		$file->method('getId')
3834
-			->willReturn(42);
3835
-
3836
-		$share = Server::get(IManager::class)->newShare();
3837
-		$share->setPermissions(Constants::PERMISSION_ALL)
3838
-			->setSharedBy($this->currentUser)
3839
-			->setShareType(IShare::TYPE_USER)
3840
-			->setNode($file);
3841
-
3842
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3843
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3844
-
3845
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3846
-			$this->callback(function (IShare $share) {
3847
-				return $share->getPermissions() === Constants::PERMISSION_ALL;
3848
-			})
3849
-		)->willReturnArgument(0);
3850
-
3851
-		$this->shareManager->method('getSharedWith')->willReturn([]);
3852
-
3853
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3854
-		$this->rootFolder->method('getUserFolder')
3855
-			->with($this->currentUser)
3856
-			->willReturn($userFolder);
3857
-
3858
-		$userFolder->method('getById')
3859
-			->with(42)
3860
-			->willReturn([$file]);
3861
-
3862
-		$mountPoint = $this->createMock(IMountPoint::class);
3863
-		$file->method('getMountPoint')
3864
-			->willReturn($mountPoint);
3865
-		$mountPoint->method('getStorageRootId')
3866
-			->willReturn(42);
3867
-
3868
-		$expected = new DataResponse([]);
3869
-		$result = $ocs->updateShare(42, 31, null, null, null, null);
3870
-
3871
-		$this->assertInstanceOf(get_class($expected), $result);
3872
-		$this->assertEquals($expected->getData(), $result->getData());
3873
-	}
3874
-
3875
-	public function testUpdateShareCannotIncreasePermissions(): void {
3876
-		$ocs = $this->mockFormatShare();
3877
-
3878
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3879
-		$folder->method('getId')
3880
-			->willReturn(42);
3881
-
3882
-		$share = Server::get(IManager::class)->newShare();
3883
-		$share
3884
-			->setId(42)
3885
-			->setSharedBy($this->currentUser)
3886
-			->setShareOwner('anotheruser')
3887
-			->setShareType(IShare::TYPE_GROUP)
3888
-			->setSharedWith('group1')
3889
-			->setPermissions(Constants::PERMISSION_READ)
3890
-			->setNode($folder);
3891
-
3892
-		// note: updateShare will modify the received instance but getSharedWith will reread from the database,
3893
-		// so their values will be different
3894
-		$incomingShare = Server::get(IManager::class)->newShare();
3895
-		$incomingShare
3896
-			->setId(42)
3897
-			->setSharedBy($this->currentUser)
3898
-			->setShareOwner('anotheruser')
3899
-			->setShareType(IShare::TYPE_GROUP)
3900
-			->setSharedWith('group1')
3901
-			->setPermissions(Constants::PERMISSION_READ)
3902
-			->setNode($folder);
3903
-
3904
-		$this->request
3905
-			->method('getParam')
3906
-			->willReturnMap([
3907
-				['permissions', null, '31'],
3908
-			]);
3909
-
3910
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3911
-
3912
-		$this->shareManager->expects($this->any())
3913
-			->method('getSharedWith')
3914
-			->willReturnMap([
3915
-				['currentUser', IShare::TYPE_USER, $share->getNode(), -1, 0, []],
3916
-				['currentUser', IShare::TYPE_GROUP, $share->getNode(), -1, 0, [$incomingShare]],
3917
-				['currentUser', IShare::TYPE_ROOM, $share->getNode(), -1, 0, []]
3918
-			]);
3919
-
3920
-		$this->rootFolder->method('getUserFolder')
3921
-			->with($this->currentUser)
3922
-			->willReturn($userFolder);
3923
-
3924
-		$userFolder->method('getById')
3925
-			->with(42)
3926
-			->willReturn([$folder]);
3927
-		$userFolder->method('getFirstNodeById')
3928
-			->with(42)
3929
-			->willReturn($folder);
3930
-
3931
-		$mountPoint = $this->createMock(IMountPoint::class);
3932
-		$folder->method('getMountPoint')
3933
-			->willReturn($mountPoint);
3934
-		$mountPoint->method('getStorageRootId')
3935
-			->willReturn(42);
3936
-
3937
-		$this->shareManager->expects($this->once())
3938
-			->method('updateShare')
3939
-			->with($share)
3940
-			->willThrowException(new GenericShareException('Cannot increase permissions of path/file', 'Cannot increase permissions of path/file', 404));
3941
-
3942
-		try {
3943
-			$ocs->updateShare(42, 31);
3944
-			$this->fail();
3945
-		} catch (OCSException $e) {
3946
-			$this->assertEquals('Cannot increase permissions of path/file', $e->getMessage());
3947
-		}
3948
-	}
3949
-
3950
-	public function testUpdateShareCanIncreasePermissionsIfOwner(): void {
3951
-		$ocs = $this->mockFormatShare();
3952
-
3953
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3954
-		$folder->method('getId')
3955
-			->willReturn(42);
3956
-
3957
-		$share = Server::get(IManager::class)->newShare();
3958
-		$share
3959
-			->setId(42)
3960
-			->setSharedBy($this->currentUser)
3961
-			->setShareOwner($this->currentUser)
3962
-			->setShareType(IShare::TYPE_GROUP)
3963
-			->setSharedWith('group1')
3964
-			->setPermissions(Constants::PERMISSION_READ)
3965
-			->setNode($folder);
3966
-
3967
-		// note: updateShare will modify the received instance but getSharedWith will reread from the database,
3968
-		// so their values will be different
3969
-		$incomingShare = Server::get(IManager::class)->newShare();
3970
-		$incomingShare
3971
-			->setId(42)
3972
-			->setSharedBy($this->currentUser)
3973
-			->setShareOwner($this->currentUser)
3974
-			->setShareType(IShare::TYPE_GROUP)
3975
-			->setSharedWith('group1')
3976
-			->setPermissions(Constants::PERMISSION_READ)
3977
-			->setNode($folder);
3978
-
3979
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3980
-
3981
-		$this->shareManager->expects($this->any())
3982
-			->method('getSharedWith')
3983
-			->willReturnMap([
3984
-				['currentUser', IShare::TYPE_USER, $share->getNode(), -1, 0, []],
3985
-				['currentUser', IShare::TYPE_GROUP, $share->getNode(), -1, 0, [$incomingShare]]
3986
-			]);
3987
-
3988
-		$this->shareManager->expects($this->once())
3989
-			->method('updateShare')
3990
-			->with($share)
3991
-			->willReturn($share);
3992
-
3993
-		$this->rootFolder->method('getUserFolder')
3994
-			->with($this->currentUser)
3995
-			->willReturn($userFolder);
3996
-
3997
-		$userFolder->method('getById')
3998
-			->with(42)
3999
-			->willReturn([$folder]);
4000
-
4001
-		$mountPoint = $this->createMock(IMountPoint::class);
4002
-		$folder->method('getMountPoint')
4003
-			->willReturn($mountPoint);
4004
-		$mountPoint->method('getStorageRootId')
4005
-			->willReturn(42);
4006
-
4007
-		$result = $ocs->updateShare(42, 31);
4008
-		$this->assertInstanceOf(DataResponse::class, $result);
4009
-	}
4010
-
4011
-	public function testUpdateShareOwnerless(): void {
4012
-		$ocs = $this->mockFormatShare();
4013
-
4014
-		$mount = $this->createMock(IShareOwnerlessMount::class);
4015
-
4016
-		$file = $this->createMock(File::class);
4017
-		$file
4018
-			->expects($this->exactly(2))
4019
-			->method('getPermissions')
4020
-			->willReturn(Constants::PERMISSION_SHARE);
4021
-		$file
4022
-			->expects($this->once())
4023
-			->method('getMountPoint')
4024
-			->willReturn($mount);
4025
-
4026
-		$userFolder = $this->createMock(Folder::class);
4027
-		$userFolder->method('getById')
4028
-			->with(2)
4029
-			->willReturn([$file]);
4030
-		$userFolder->method('getFirstNodeById')
4031
-			->with(2)
4032
-			->willReturn($file);
4033
-
4034
-		$this->rootFolder
4035
-			->method('getUserFolder')
4036
-			->with($this->currentUser)
4037
-			->willReturn($userFolder);
4038
-
4039
-		$share = $this->createMock(IShare::class);
4040
-		$share
4041
-			->expects($this->once())
4042
-			->method('getNode')
4043
-			->willReturn($file);
4044
-		$share
4045
-			->expects($this->exactly(2))
4046
-			->method('getNodeId')
4047
-			->willReturn(2);
4048
-		$share
4049
-			->expects($this->exactly(2))
4050
-			->method('getPermissions')
4051
-			->willReturn(Constants::PERMISSION_SHARE);
4052
-
4053
-		$this->shareManager
4054
-			->expects($this->once())
4055
-			->method('getShareById')
4056
-			->with('ocinternal:1', $this->currentUser)
4057
-			->willReturn($share);
4058
-
4059
-		$this->shareManager
4060
-			->expects($this->once())
4061
-			->method('updateShare')
4062
-			->with($share)
4063
-			->willReturn($share);
4064
-
4065
-		$result = $ocs->updateShare(1, Constants::PERMISSION_ALL);
4066
-		$this->assertInstanceOf(DataResponse::class, $result);
4067
-	}
4068
-
4069
-	public static function dataFormatShare(): array {
4070
-		$owner = ['getDisplayName' => 'ownerDN'];
4071
-		$initiator = ['getDisplayName' => 'initiatorDN'];
4072
-		$recipient = [
4073
-			'getDisplayName' => 'recipientDN',
4074
-			'getSystemEMailAddress' => 'recipient'
4075
-		];
4076
-
4077
-		$folder = [
4078
-			'class' => Folder::class,
4079
-			'mimeType' => 'myFolderMimeType',
4080
-			'path' => 'folder',
4081
-			'id' => 2,
4082
-		];
4083
-		$file = [
4084
-			'class' => File::class,
4085
-			'mimeType' => 'myMimeType',
4086
-			'path' => 'file',
4087
-			'id' => 3,
4088
-		];
4089
-		$fileWithPreview = [
4090
-			'class' => File::class,
4091
-			'mimeType' => 'mimeWithPreview',
4092
-			'path' => 'fileWithPreview',
4093
-			'id' => 4,
4094
-		];
4095
-
4096
-		$result = [];
4097
-
4098
-		$share = [
4099
-			'type' => IShare::TYPE_USER,
4100
-			'owner' => 'owner',
4101
-			'sharedWith' => 'recipient',
4102
-			'attributes' => [
4103
-				'scope' => 'permissions',
4104
-				'key' => 'download',
4105
-				'value' => true
4106
-			],
4107
-			'node' => $file,
4108
-			'note' => 'personal note',
4109
-		];
4110
-
4111
-		// User backend down
4112
-		$result[] = [
4113
-			[
4114
-				'id' => '42',
4115
-				'share_type' => IShare::TYPE_USER,
4116
-				'uid_owner' => 'initiator',
4117
-				'displayname_owner' => 'initiator',
4118
-				'permissions' => 1,
4119
-				'stime' => 946684862,
4120
-				'parent' => null,
4121
-				'expiration' => null,
4122
-				'token' => null,
4123
-				'uid_file_owner' => 'owner',
4124
-				'displayname_file_owner' => 'owner',
4125
-				'path' => 'file',
4126
-				'item_type' => 'file',
4127
-				'storage_id' => 'storageId',
4128
-				'storage' => 100,
4129
-				'item_source' => 3,
4130
-				'file_source' => 3,
4131
-				'file_parent' => 1,
4132
-				'file_target' => 'myTarget',
4133
-				'share_with' => 'recipient',
4134
-				'share_with_displayname' => 'recipient',
4135
-				'share_with_displayname_unique' => 'recipient',
4136
-				'note' => 'personal note',
4137
-				'label' => '',
4138
-				'mail_send' => 0,
4139
-				'mimetype' => 'myMimeType',
4140
-				'has_preview' => false,
4141
-				'hide_download' => 0,
4142
-				'can_edit' => false,
4143
-				'can_delete' => false,
4144
-				'item_size' => 123456,
4145
-				'item_mtime' => 1234567890,
4146
-				'is-mount-root' => false,
4147
-				'mount-type' => '',
4148
-				'attributes' => '[{"scope":"permissions","key":"download","value":true}]',
4149
-				'item_permissions' => 1,
4150
-			],
4151
-			$share,
4152
-			[], false
4153
-		];
4154
-		// User backend up
4155
-		$result[] = [
4156
-			[
4157
-				'id' => '42',
4158
-				'share_type' => IShare::TYPE_USER,
4159
-				'uid_owner' => 'initiator',
4160
-				'displayname_owner' => 'initiatorDN',
4161
-				'permissions' => 1,
4162
-				'stime' => 946684862,
4163
-				'parent' => null,
4164
-				'expiration' => null,
4165
-				'token' => null,
4166
-				'uid_file_owner' => 'owner',
4167
-				'displayname_file_owner' => 'ownerDN',
4168
-				'note' => 'personal note',
4169
-				'label' => '',
4170
-				'path' => 'file',
4171
-				'item_type' => 'file',
4172
-				'storage_id' => 'storageId',
4173
-				'storage' => 100,
4174
-				'item_source' => 3,
4175
-				'file_source' => 3,
4176
-				'file_parent' => 1,
4177
-				'file_target' => 'myTarget',
4178
-				'share_with' => 'recipient',
4179
-				'share_with_displayname' => 'recipientDN',
4180
-				'share_with_displayname_unique' => 'recipient',
4181
-				'mail_send' => 0,
4182
-				'mimetype' => 'myMimeType',
4183
-				'has_preview' => false,
4184
-				'hide_download' => 0,
4185
-				'can_edit' => false,
4186
-				'can_delete' => false,
4187
-				'item_size' => 123456,
4188
-				'item_mtime' => 1234567890,
4189
-				'is-mount-root' => false,
4190
-				'mount-type' => '',
4191
-				'attributes' => '[{"scope":"permissions","key":"download","value":true}]',
4192
-				'item_permissions' => 1,
4193
-			], $share, [
4194
-				['owner', $owner],
4195
-				['initiator', $initiator],
4196
-				['recipient', $recipient],
4197
-			], false
4198
-		];
4199
-
4200
-		// Same but no attributes
4201
-		$share = [
4202
-			'type' => IShare::TYPE_USER,
4203
-			'owner' => 'owner',
4204
-			'sharedWith' => 'recipient',
4205
-			'node' => $file,
4206
-			'note' => 'personal note',
4207
-		];
4208
-
4209
-		// User backend down
4210
-		$result[] = [
4211
-			[
4212
-				'id' => '42',
4213
-				'share_type' => IShare::TYPE_USER,
4214
-				'uid_owner' => 'initiator',
4215
-				'displayname_owner' => 'initiator',
4216
-				'permissions' => 1,
4217
-				'attributes' => null,
4218
-				'stime' => 946684862,
4219
-				'parent' => null,
4220
-				'expiration' => null,
4221
-				'token' => null,
4222
-				'uid_file_owner' => 'owner',
4223
-				'displayname_file_owner' => 'owner',
4224
-				'note' => 'personal note',
4225
-				'label' => '',
4226
-				'path' => 'file',
4227
-				'item_type' => 'file',
4228
-				'storage_id' => 'storageId',
4229
-				'storage' => 100,
4230
-				'item_source' => 3,
4231
-				'file_source' => 3,
4232
-				'file_parent' => 1,
4233
-				'file_target' => 'myTarget',
4234
-				'share_with' => 'recipient',
4235
-				'share_with_displayname' => 'recipient',
4236
-				'share_with_displayname_unique' => 'recipient',
4237
-				'mail_send' => 0,
4238
-				'mimetype' => 'myMimeType',
4239
-				'has_preview' => false,
4240
-				'hide_download' => 0,
4241
-				'can_edit' => false,
4242
-				'can_delete' => false,
4243
-				'item_size' => 123456,
4244
-				'item_mtime' => 1234567890,
4245
-				'is-mount-root' => false,
4246
-				'mount-type' => '',
4247
-				'attributes' => null,
4248
-				'item_permissions' => 1,
4249
-			], $share, [], false
4250
-		];
4251
-
4252
-		$share['owner'] = 'currentUser';
4253
-
4254
-		// User backend down
4255
-		$result[] = [
4256
-			[
4257
-				'id' => '42',
4258
-				'share_type' => IShare::TYPE_USER,
4259
-				'uid_owner' => 'initiator',
4260
-				'displayname_owner' => 'initiator',
4261
-				'permissions' => 1,
4262
-				'attributes' => null,
4263
-				'stime' => 946684862,
4264
-				'parent' => null,
4265
-				'expiration' => null,
4266
-				'token' => null,
4267
-				'uid_file_owner' => 'currentUser',
4268
-				'displayname_file_owner' => 'currentUser',
4269
-				'note' => 'personal note',
4270
-				'label' => '',
4271
-				'path' => 'file',
4272
-				'item_type' => 'file',
4273
-				'storage_id' => 'storageId',
4274
-				'storage' => 100,
4275
-				'item_source' => 3,
4276
-				'file_source' => 3,
4277
-				'file_parent' => 1,
4278
-				'file_target' => 'myTarget',
4279
-				'share_with' => 'recipient',
4280
-				'share_with_displayname' => 'recipient',
4281
-				'share_with_displayname_unique' => 'recipient',
4282
-				'mail_send' => 0,
4283
-				'mimetype' => 'myMimeType',
4284
-				'has_preview' => false,
4285
-				'hide_download' => 0,
4286
-				'can_edit' => true,
4287
-				'can_delete' => true,
4288
-				'item_size' => 123456,
4289
-				'item_mtime' => 1234567890,
4290
-				'is-mount-root' => false,
4291
-				'mount-type' => '',
4292
-				'attributes' => null,
4293
-				'item_permissions' => 11,
4294
-			], $share, [], false
4295
-		];
4296
-
4297
-		// with existing group
4298
-		$share = [
4299
-			'type' => IShare::TYPE_GROUP,
4300
-			'owner' => 'owner',
4301
-			'sharedWith' => 'recipientGroup',
4302
-			'node' => $file,
4303
-			'note' => 'personal note',
4304
-		];
4305
-
4306
-		$result[] = [
4307
-			[
4308
-				'id' => '42',
4309
-				'share_type' => IShare::TYPE_GROUP,
4310
-				'uid_owner' => 'initiator',
4311
-				'displayname_owner' => 'initiator',
4312
-				'permissions' => 1,
4313
-				'attributes' => null,
4314
-				'stime' => 946684862,
4315
-				'parent' => null,
4316
-				'expiration' => null,
4317
-				'token' => null,
4318
-				'uid_file_owner' => 'owner',
4319
-				'displayname_file_owner' => 'owner',
4320
-				'note' => 'personal note',
4321
-				'label' => '',
4322
-				'path' => 'file',
4323
-				'item_type' => 'file',
4324
-				'storage_id' => 'storageId',
4325
-				'storage' => 100,
4326
-				'item_source' => 3,
4327
-				'file_source' => 3,
4328
-				'file_parent' => 1,
4329
-				'file_target' => 'myTarget',
4330
-				'share_with' => 'recipientGroup',
4331
-				'share_with_displayname' => 'recipientGroupDisplayName',
4332
-				'mail_send' => 0,
4333
-				'mimetype' => 'myMimeType',
4334
-				'has_preview' => false,
4335
-				'hide_download' => 0,
4336
-				'can_edit' => false,
4337
-				'can_delete' => false,
4338
-				'item_size' => 123456,
4339
-				'item_mtime' => 1234567890,
4340
-				'is-mount-root' => false,
4341
-				'mount-type' => '',
4342
-				'attributes' => null,
4343
-				'item_permissions' => 1,
4344
-			], $share, [], false
4345
-		];
4346
-
4347
-		// with unknown group / no group backend
4348
-		$share['sharedWith'] = 'recipientGroup2';
4349
-
4350
-		$result[] = [
4351
-			[
4352
-				'id' => '42',
4353
-				'share_type' => IShare::TYPE_GROUP,
4354
-				'uid_owner' => 'initiator',
4355
-				'displayname_owner' => 'initiator',
4356
-				'permissions' => 1,
4357
-				'stime' => 946684862,
4358
-				'parent' => null,
4359
-				'expiration' => null,
4360
-				'token' => null,
4361
-				'uid_file_owner' => 'owner',
4362
-				'displayname_file_owner' => 'owner',
4363
-				'note' => 'personal note',
4364
-				'label' => '',
4365
-				'path' => 'file',
4366
-				'item_type' => 'file',
4367
-				'storage_id' => 'storageId',
4368
-				'storage' => 100,
4369
-				'item_source' => 3,
4370
-				'file_source' => 3,
4371
-				'file_parent' => 1,
4372
-				'file_target' => 'myTarget',
4373
-				'share_with' => 'recipientGroup2',
4374
-				'share_with_displayname' => 'recipientGroup2',
4375
-				'mail_send' => 0,
4376
-				'mimetype' => 'myMimeType',
4377
-				'has_preview' => false,
4378
-				'hide_download' => 0,
4379
-				'can_edit' => false,
4380
-				'can_delete' => false,
4381
-				'item_size' => 123456,
4382
-				'item_mtime' => 1234567890,
4383
-				'is-mount-root' => false,
4384
-				'mount-type' => '',
4385
-				'attributes' => null,
4386
-				'item_permissions' => 1,
4387
-			], $share, [], false
4388
-		];
4389
-
4390
-		$share = [
4391
-			'type' => IShare::TYPE_LINK,
4392
-			'owner' => 'owner',
4393
-			'node' => $file,
4394
-			'note' => 'personal note',
4395
-			'password' => 'mypassword',
4396
-			'expirationDate' => new \DateTime('2001-01-02T00:00:00'),
4397
-			'token' => 'myToken',
4398
-			'label' => 'new link share',
4399
-		];
4400
-
4401
-		$result[] = [
4402
-			[
4403
-				'id' => '42',
4404
-				'share_type' => IShare::TYPE_LINK,
4405
-				'uid_owner' => 'initiator',
4406
-				'displayname_owner' => 'initiator',
4407
-				'permissions' => 1,
4408
-				'attributes' => null,
4409
-				'stime' => 946684862,
4410
-				'parent' => null,
4411
-				'expiration' => '2001-01-02 00:00:00',
4412
-				'token' => 'myToken',
4413
-				'uid_file_owner' => 'owner',
4414
-				'displayname_file_owner' => 'owner',
4415
-				'note' => 'personal note',
4416
-				'label' => 'new link share',
4417
-				'path' => 'file',
4418
-				'item_type' => 'file',
4419
-				'storage_id' => 'storageId',
4420
-				'storage' => 100,
4421
-				'item_source' => 3,
4422
-				'file_source' => 3,
4423
-				'file_parent' => 1,
4424
-				'file_target' => 'myTarget',
4425
-				'password' => 'mypassword',
4426
-				'share_with' => 'mypassword',
4427
-				'share_with_displayname' => '(Shared link)',
4428
-				'send_password_by_talk' => false,
4429
-				'mail_send' => 0,
4430
-				'url' => 'myLink',
4431
-				'mimetype' => 'myMimeType',
4432
-				'has_preview' => false,
4433
-				'hide_download' => 0,
4434
-				'can_edit' => false,
4435
-				'can_delete' => false,
4436
-				'item_size' => 123456,
4437
-				'item_mtime' => 1234567890,
4438
-				'is-mount-root' => false,
4439
-				'mount-type' => '',
4440
-				'attributes' => null,
4441
-				'item_permissions' => 1,
4442
-			], $share, [], false
4443
-		];
4444
-
4445
-		$share['sendPasswordByTalk'] = true;
4446
-
4447
-		$result[] = [
4448
-			[
4449
-				'id' => '42',
4450
-				'share_type' => IShare::TYPE_LINK,
4451
-				'uid_owner' => 'initiator',
4452
-				'displayname_owner' => 'initiator',
4453
-				'permissions' => 1,
4454
-				'stime' => 946684862,
4455
-				'parent' => null,
4456
-				'expiration' => '2001-01-02 00:00:00',
4457
-				'token' => 'myToken',
4458
-				'uid_file_owner' => 'owner',
4459
-				'displayname_file_owner' => 'owner',
4460
-				'note' => 'personal note',
4461
-				'label' => 'new link share',
4462
-				'path' => 'file',
4463
-				'item_type' => 'file',
4464
-				'storage_id' => 'storageId',
4465
-				'storage' => 100,
4466
-				'item_source' => 3,
4467
-				'file_source' => 3,
4468
-				'file_parent' => 1,
4469
-				'file_target' => 'myTarget',
4470
-				'password' => 'mypassword',
4471
-				'share_with' => 'mypassword',
4472
-				'share_with_displayname' => '(Shared link)',
4473
-				'send_password_by_talk' => true,
4474
-				'mail_send' => 0,
4475
-				'url' => 'myLink',
4476
-				'mimetype' => 'myMimeType',
4477
-				'has_preview' => false,
4478
-				'hide_download' => 0,
4479
-				'can_edit' => false,
4480
-				'can_delete' => false,
4481
-				'item_size' => 123456,
4482
-				'item_mtime' => 1234567890,
4483
-				'is-mount-root' => false,
4484
-				'mount-type' => '',
4485
-				'attributes' => null,
4486
-				'item_permissions' => 1,
4487
-			], $share, [], false
4488
-		];
4489
-
4490
-		$share = [
4491
-			'type' => IShare::TYPE_REMOTE,
4492
-			'owner' => 'owner',
4493
-			'sharedWith' => '[email protected]',
4494
-			'node' => $folder,
4495
-			'note' => 'personal note',
4496
-			'expirationDate' => new \DateTime('2001-02-03T04:05:06'),
4497
-		];
4498
-
4499
-		$result[] = [
4500
-			[
4501
-				'id' => '42',
4502
-				'share_type' => IShare::TYPE_REMOTE,
4503
-				'uid_owner' => 'initiator',
4504
-				'displayname_owner' => 'initiator',
4505
-				'permissions' => 1,
4506
-				'stime' => 946684862,
4507
-				'parent' => null,
4508
-				'expiration' => '2001-02-03 00:00:00',
4509
-				'token' => null,
4510
-				'uid_file_owner' => 'owner',
4511
-				'displayname_file_owner' => 'owner',
4512
-				'note' => 'personal note',
4513
-				'label' => '',
4514
-				'path' => 'folder',
4515
-				'item_type' => 'folder',
4516
-				'storage_id' => 'storageId',
4517
-				'storage' => 100,
4518
-				'item_source' => 2,
4519
-				'file_source' => 2,
4520
-				'file_parent' => 1,
4521
-				'file_target' => 'myTarget',
4522
-				'share_with' => '[email protected]',
4523
-				'share_with_displayname' => 'foobar',
4524
-				'mail_send' => 0,
4525
-				'mimetype' => 'myFolderMimeType',
4526
-				'has_preview' => false,
4527
-				'hide_download' => 0,
4528
-				'can_edit' => false,
4529
-				'can_delete' => false,
4530
-				'item_size' => 123456,
4531
-				'item_mtime' => 1234567890,
4532
-				'is-mount-root' => false,
4533
-				'mount-type' => '',
4534
-				'attributes' => null,
4535
-				'item_permissions' => 1,
4536
-				'is_trusted_server' => false,
4537
-			], $share, [], false
4538
-		];
4539
-
4540
-		$share = [
4541
-			'type' => IShare::TYPE_REMOTE_GROUP,
4542
-			'owner' => 'owner',
4543
-			'sharedWith' => '[email protected]',
4544
-			'node' => $folder,
4545
-			'note' => 'personal note',
4546
-			'expirationDate' => new \DateTime('2001-02-03T04:05:06'),
4547
-		];
4548
-
4549
-		$result[] = [
4550
-			[
4551
-				'id' => '42',
4552
-				'share_type' => IShare::TYPE_REMOTE_GROUP,
4553
-				'uid_owner' => 'initiator',
4554
-				'displayname_owner' => 'initiator',
4555
-				'permissions' => 1,
4556
-				'stime' => 946684862,
4557
-				'parent' => null,
4558
-				'expiration' => '2001-02-03 00:00:00',
4559
-				'token' => null,
4560
-				'uid_file_owner' => 'owner',
4561
-				'displayname_file_owner' => 'owner',
4562
-				'note' => 'personal note',
4563
-				'label' => '',
4564
-				'path' => 'folder',
4565
-				'item_type' => 'folder',
4566
-				'storage_id' => 'storageId',
4567
-				'storage' => 100,
4568
-				'item_source' => 2,
4569
-				'file_source' => 2,
4570
-				'file_parent' => 1,
4571
-				'file_target' => 'myTarget',
4572
-				'share_with' => '[email protected]',
4573
-				'share_with_displayname' => 'foobar',
4574
-				'mail_send' => 0,
4575
-				'mimetype' => 'myFolderMimeType',
4576
-				'has_preview' => false,
4577
-				'hide_download' => 0,
4578
-				'can_edit' => false,
4579
-				'can_delete' => false,
4580
-				'item_size' => 123456,
4581
-				'item_mtime' => 1234567890,
4582
-				'is-mount-root' => false,
4583
-				'mount-type' => '',
4584
-				'attributes' => null,
4585
-				'item_permissions' => 1,
4586
-				'is_trusted_server' => false,
4587
-			], $share, [], false
4588
-		];
4589
-
4590
-		// Circle with id, display name and avatar set by the Circles app
4591
-		$share = [
4592
-			'type' => IShare::TYPE_CIRCLE,
4593
-			'owner' => 'owner',
4594
-			'sharedWith' => 'Circle (Public circle, circleOwner) [4815162342]',
4595
-			'sharedWithDisplayName' => 'The display name',
4596
-			'sharedWithAvatar' => 'path/to/the/avatar',
4597
-			'node' => $folder,
4598
-		];
4599
-
4600
-		$result[] = [
4601
-			[
4602
-				'id' => '42',
4603
-				'share_type' => IShare::TYPE_CIRCLE,
4604
-				'uid_owner' => 'initiator',
4605
-				'displayname_owner' => 'initiator',
4606
-				'permissions' => 1,
4607
-				'attributes' => null,
4608
-				'stime' => 946684862,
4609
-				'parent' => null,
4610
-				'expiration' => null,
4611
-				'token' => null,
4612
-				'uid_file_owner' => 'owner',
4613
-				'displayname_file_owner' => 'owner',
4614
-				'note' => '',
4615
-				'label' => '',
4616
-				'path' => 'folder',
4617
-				'item_type' => 'folder',
4618
-				'storage_id' => 'storageId',
4619
-				'storage' => 100,
4620
-				'item_source' => 2,
4621
-				'file_source' => 2,
4622
-				'file_parent' => 1,
4623
-				'file_target' => 'myTarget',
4624
-				'share_with' => '4815162342',
4625
-				'share_with_displayname' => 'The display name',
4626
-				'share_with_avatar' => 'path/to/the/avatar',
4627
-				'mail_send' => 0,
4628
-				'mimetype' => 'myFolderMimeType',
4629
-				'has_preview' => false,
4630
-				'hide_download' => 0,
4631
-				'can_edit' => false,
4632
-				'can_delete' => false,
4633
-				'item_size' => 123456,
4634
-				'item_mtime' => 1234567890,
4635
-				'is-mount-root' => false,
4636
-				'mount-type' => '',
4637
-				'attributes' => null,
4638
-				'item_permissions' => 1,
4639
-			], $share, [], false
4640
-		];
4641
-
4642
-		// Circle with id set by the Circles app
4643
-		$share = [
4644
-			'type' => IShare::TYPE_CIRCLE,
4645
-			'owner' => 'owner',
4646
-			'sharedWith' => 'Circle (Public circle, circleOwner) [4815162342]',
4647
-			'node' => $folder,
4648
-		];
4649
-
4650
-		$result[] = [
4651
-			[
4652
-				'id' => '42',
4653
-				'share_type' => IShare::TYPE_CIRCLE,
4654
-				'uid_owner' => 'initiator',
4655
-				'displayname_owner' => 'initiator',
4656
-				'permissions' => 1,
4657
-				'stime' => 946684862,
4658
-				'parent' => null,
4659
-				'expiration' => null,
4660
-				'token' => null,
4661
-				'uid_file_owner' => 'owner',
4662
-				'displayname_file_owner' => 'owner',
4663
-				'note' => '',
4664
-				'label' => '',
4665
-				'path' => 'folder',
4666
-				'item_type' => 'folder',
4667
-				'storage_id' => 'storageId',
4668
-				'storage' => 100,
4669
-				'item_source' => 2,
4670
-				'file_source' => 2,
4671
-				'file_parent' => 1,
4672
-				'file_target' => 'myTarget',
4673
-				'share_with' => '4815162342',
4674
-				'share_with_displayname' => 'Circle (Public circle, circleOwner)',
4675
-				'share_with_avatar' => '',
4676
-				'mail_send' => 0,
4677
-				'mimetype' => 'myFolderMimeType',
4678
-				'has_preview' => false,
4679
-				'hide_download' => 0,
4680
-				'can_edit' => false,
4681
-				'can_delete' => false,
4682
-				'item_size' => 123456,
4683
-				'item_mtime' => 1234567890,
4684
-				'is-mount-root' => false,
4685
-				'mount-type' => '',
4686
-				'attributes' => null,
4687
-				'item_permissions' => 1,
4688
-			], $share, [], false
4689
-		];
4690
-
4691
-		// Circle with id not set by the Circles app
4692
-		$share = [
4693
-			'type' => IShare::TYPE_CIRCLE,
4694
-			'owner' => 'owner',
4695
-			'sharedWith' => 'Circle (Public circle, circleOwner)',
4696
-			'node' => $folder,
4697
-		];
4698
-
4699
-		$result[] = [
4700
-			[
4701
-				'id' => '42',
4702
-				'share_type' => IShare::TYPE_CIRCLE,
4703
-				'uid_owner' => 'initiator',
4704
-				'displayname_owner' => 'initiator',
4705
-				'permissions' => 1,
4706
-				'stime' => 946684862,
4707
-				'parent' => null,
4708
-				'expiration' => null,
4709
-				'token' => null,
4710
-				'uid_file_owner' => 'owner',
4711
-				'displayname_file_owner' => 'owner',
4712
-				'note' => '',
4713
-				'label' => '',
4714
-				'path' => 'folder',
4715
-				'item_type' => 'folder',
4716
-				'storage_id' => 'storageId',
4717
-				'storage' => 100,
4718
-				'item_source' => 2,
4719
-				'file_source' => 2,
4720
-				'file_parent' => 1,
4721
-				'file_target' => 'myTarget',
4722
-				'share_with' => 'Circle',
4723
-				'share_with_displayname' => 'Circle (Public circle, circleOwner)',
4724
-				'share_with_avatar' => '',
4725
-				'mail_send' => 0,
4726
-				'mimetype' => 'myFolderMimeType',
4727
-				'has_preview' => false,
4728
-				'hide_download' => 0,
4729
-				'can_edit' => false,
4730
-				'can_delete' => false,
4731
-				'item_size' => 123456,
4732
-				'item_mtime' => 1234567890,
4733
-				'is-mount-root' => false,
4734
-				'mount-type' => '',
4735
-				'attributes' => null,
4736
-				'item_permissions' => 1,
4737
-			], $share, [], false
4738
-		];
4739
-
4740
-		// No node
4741
-		$share = [
4742
-			'type' => IShare::TYPE_USER,
4743
-			'owner' => 'owner',
4744
-			'sharedWith' => 'recipient',
4745
-			'note' => 'personal note',
4746
-		];
4747
-
4748
-		$result[] = [
4749
-			[], $share, [], true
4750
-		];
4751
-
4752
-		$share = [
4753
-			'type' => IShare::TYPE_EMAIL,
4754
-			'owner' => 'owner',
4755
-			'sharedWith' => '[email protected]',
4756
-			'node' => $folder,
4757
-			'password' => 'password',
4758
-		];
4759
-
4760
-		$result[] = [
4761
-			[
4762
-				'id' => '42',
4763
-				'share_type' => IShare::TYPE_EMAIL,
4764
-				'uid_owner' => 'initiator',
4765
-				'displayname_owner' => 'initiator',
4766
-				'permissions' => 1,
4767
-				'stime' => 946684862,
4768
-				'parent' => null,
4769
-				'expiration' => null,
4770
-				'token' => null,
4771
-				'uid_file_owner' => 'owner',
4772
-				'displayname_file_owner' => 'owner',
4773
-				'note' => '',
4774
-				'label' => '',
4775
-				'path' => 'folder',
4776
-				'item_type' => 'folder',
4777
-				'storage_id' => 'storageId',
4778
-				'storage' => 100,
4779
-				'item_source' => 2,
4780
-				'file_source' => 2,
4781
-				'file_parent' => 1,
4782
-				'file_target' => 'myTarget',
4783
-				'share_with' => '[email protected]',
4784
-				'share_with_displayname' => 'mail display name',
4785
-				'mail_send' => 0,
4786
-				'mimetype' => 'myFolderMimeType',
4787
-				'has_preview' => false,
4788
-				'password' => 'password',
4789
-				'send_password_by_talk' => false,
4790
-				'hide_download' => 0,
4791
-				'can_edit' => false,
4792
-				'can_delete' => false,
4793
-				'password_expiration_time' => null,
4794
-				'item_size' => 123456,
4795
-				'item_mtime' => 1234567890,
4796
-				'is-mount-root' => false,
4797
-				'mount-type' => '',
4798
-				'attributes' => null,
4799
-				'item_permissions' => 1,
4800
-			], $share, [], false
4801
-		];
4802
-
4803
-		$share['sendPasswordByTalk'] = true;
4804
-
4805
-		$result[] = [
4806
-			[
4807
-				'id' => '42',
4808
-				'share_type' => IShare::TYPE_EMAIL,
4809
-				'uid_owner' => 'initiator',
4810
-				'displayname_owner' => 'initiator',
4811
-				'permissions' => 1,
4812
-				'stime' => 946684862,
4813
-				'parent' => null,
4814
-				'expiration' => null,
4815
-				'token' => null,
4816
-				'uid_file_owner' => 'owner',
4817
-				'displayname_file_owner' => 'owner',
4818
-				'note' => '',
4819
-				'label' => '',
4820
-				'path' => 'folder',
4821
-				'item_type' => 'folder',
4822
-				'storage_id' => 'storageId',
4823
-				'storage' => 100,
4824
-				'item_source' => 2,
4825
-				'file_source' => 2,
4826
-				'file_parent' => 1,
4827
-				'file_target' => 'myTarget',
4828
-				'share_with' => '[email protected]',
4829
-				'share_with_displayname' => 'mail display name',
4830
-				'mail_send' => 0,
4831
-				'mimetype' => 'myFolderMimeType',
4832
-				'has_preview' => false,
4833
-				'password' => 'password',
4834
-				'send_password_by_talk' => true,
4835
-				'hide_download' => 0,
4836
-				'can_edit' => false,
4837
-				'can_delete' => false,
4838
-				'password_expiration_time' => null,
4839
-				'item_size' => 123456,
4840
-				'item_mtime' => 1234567890,
4841
-				'is-mount-root' => false,
4842
-				'mount-type' => '',
4843
-				'attributes' => null,
4844
-				'item_permissions' => 1,
4845
-			], $share, [], false
4846
-		];
4847
-
4848
-		// Preview is available
4849
-		$share = [
4850
-			'type' => IShare::TYPE_USER,
4851
-			'owner' => 'currentUser',
4852
-			'sharedWith' => 'recipient',
4853
-			'node' => $fileWithPreview,
4854
-			'note' => 'personal note',
4855
-		];
4856
-
4857
-		$result[] = [
4858
-			[
4859
-				'id' => '42',
4860
-				'share_type' => IShare::TYPE_USER,
4861
-				'uid_owner' => 'initiator',
4862
-				'displayname_owner' => 'initiator',
4863
-				'permissions' => 1,
4864
-				'stime' => 946684862,
4865
-				'parent' => null,
4866
-				'expiration' => null,
4867
-				'token' => null,
4868
-				'uid_file_owner' => 'currentUser',
4869
-				'displayname_file_owner' => 'currentUser',
4870
-				'note' => 'personal note',
4871
-				'label' => '',
4872
-				'path' => 'fileWithPreview',
4873
-				'item_type' => 'file',
4874
-				'storage_id' => 'storageId',
4875
-				'storage' => 100,
4876
-				'item_source' => 4,
4877
-				'file_source' => 4,
4878
-				'file_parent' => 1,
4879
-				'file_target' => 'myTarget',
4880
-				'share_with' => 'recipient',
4881
-				'share_with_displayname' => 'recipient',
4882
-				'share_with_displayname_unique' => 'recipient',
4883
-				'mail_send' => 0,
4884
-				'mimetype' => 'mimeWithPreview',
4885
-				'has_preview' => true,
4886
-				'hide_download' => 0,
4887
-				'can_edit' => true,
4888
-				'can_delete' => true,
4889
-				'item_size' => 123456,
4890
-				'item_mtime' => 1234567890,
4891
-				'is-mount-root' => false,
4892
-				'mount-type' => '',
4893
-				'attributes' => null,
4894
-				'item_permissions' => 11,
4895
-			], $share, [], false
4896
-		];
4897
-
4898
-		return $result;
4899
-	}
4900
-
4901
-	#[DataProvider('dataFormatShare')]
4902
-	public function testFormatShare(
4903
-		array $expects,
4904
-		array $shareParams,
4905
-		array $users,
4906
-		bool $exception,
4907
-	): void {
4908
-		$users = array_map(
4909
-			function ($user) {
4910
-				$mock = $this->createMock(IUser::class);
4911
-				foreach ($user[1] as $method => $return) {
4912
-					$mock->method($method)->willReturn($return);
4913
-				}
4914
-				return [$user[0],$mock];
4915
-			},
4916
-			$users
4917
-		);
4918
-
4919
-		$share = Server::get(IManager::class)->newShare();
4920
-		$share->setShareType($shareParams['type'])
4921
-			->setSharedBy('initiator')
4922
-			->setShareOwner($shareParams['owner'])
4923
-			->setPermissions(Constants::PERMISSION_READ)
4924
-			->setShareTime(new \DateTime('2000-01-01T00:01:02'))
4925
-			->setTarget('myTarget')
4926
-			->setId(42);
4927
-		if (isset($shareParams['sharedWith'])) {
4928
-			$share->setSharedWith($shareParams['sharedWith']);
4929
-		}
4930
-		if (isset($shareParams['sharedWithDisplayName'])) {
4931
-			$share->setSharedWithDisplayName($shareParams['sharedWithDisplayName']);
4932
-		}
4933
-		if (isset($shareParams['sharedWithAvatar'])) {
4934
-			$share->setSharedWithAvatar($shareParams['sharedWithAvatar']);
4935
-		}
4936
-		if (isset($shareParams['attributes'])) {
4937
-			$shareAttributes = $this->createMock(IShareAttributes::class);
4938
-			$shareAttributes->method('toArray')->willReturn($shareParams['attributes']);
4939
-			$shareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
4940
-			$share->setAttributes($shareAttributes);
4941
-
4942
-			$expects['attributes'] = \json_encode($shareParams['attributes']);
4943
-		}
4944
-		if (isset($shareParams['node'])) {
4945
-			$node = $this->createMock($shareParams['node']['class']);
4946
-
4947
-			$node->method('getMimeType')->willReturn($shareParams['node']['mimeType']);
4948
-
4949
-			$mountPoint = $this->createMock(IMountPoint::class);
4950
-			$mountPoint->method('getMountType')->willReturn('');
4951
-			$node->method('getMountPoint')->willReturn($mountPoint);
4952
-
4953
-			$node->method('getPath')->willReturn($shareParams['node']['path']);
4954
-			$node->method('getId')->willReturn($shareParams['node']['id']);
4955
-
4956
-			$parent = $this->createMock(Folder::class);
4957
-			$parent->method('getId')->willReturn(1);
4958
-			$node->method('getParent')->willReturn($parent);
4959
-
4960
-			$node->method('getSize')->willReturn(123456);
4961
-			$node->method('getMTime')->willReturn(1234567890);
4962
-
4963
-			$cache = $this->createMock(ICache::class);
4964
-			$cache->method('getNumericStorageId')->willReturn(100);
4965
-			$storage = $this->createMock(IStorage::class);
4966
-			$storage->method('getId')->willReturn('storageId');
4967
-			$storage->method('getCache')->willReturn($cache);
4968
-
4969
-			$node->method('getStorage')->willReturn($storage);
4970
-
4971
-			$share->setNode($node);
4972
-		}
4973
-		if (isset($shareParams['note'])) {
4974
-			$share->setNote($shareParams['note']);
4975
-		}
4976
-		if (isset($shareParams['expirationDate'])) {
4977
-			$share->setExpirationDate($shareParams['expirationDate']);
4978
-		}
4979
-		if (isset($shareParams['token'])) {
4980
-			$share->setToken($shareParams['token']);
4981
-		}
4982
-		if (isset($shareParams['label'])) {
4983
-			$share->setLabel($shareParams['label']);
4984
-		}
4985
-		if (isset($shareParams['password'])) {
4986
-			$share->setPassword($shareParams['password']);
4987
-		}
4988
-		if (isset($shareParams['sendPasswordByTalk'])) {
4989
-			$share->setSendPasswordByTalk($shareParams['sendPasswordByTalk']);
4990
-		}
4991
-
4992
-		$this->userManager->method('get')->willReturnMap($users);
4993
-
4994
-		$recipientGroup = $this->createMock(IGroup::class);
4995
-		$recipientGroup->method('getDisplayName')->willReturn('recipientGroupDisplayName');
4996
-		$this->groupManager->method('get')->willReturnMap([
4997
-			['recipientGroup', $recipientGroup],
4998
-		]);
4999
-
5000
-		$this->urlGenerator->method('linkToRouteAbsolute')
5001
-			->with('files_sharing.sharecontroller.showShare', ['token' => 'myToken'])
5002
-			->willReturn('myLink');
5003
-
5004
-		$this->rootFolder->method('getUserFolder')
5005
-			->with($this->currentUser)
5006
-			->willReturnSelf();
5007
-		$this->dateTimeZone->method('getTimezone')->willReturn(new \DateTimeZone('UTC'));
5008
-
5009
-		if (!$exception) {
5010
-			$this->rootFolder->method('getFirstNodeById')
5011
-				->with($share->getNodeId())
5012
-				->willReturn($share->getNode());
5013
-
5014
-			$this->rootFolder->method('getRelativePath')
5015
-				->with($share->getNode()->getPath())
5016
-				->willReturnArgument(0);
5017
-		}
5018
-
5019
-		$cm = $this->createMock(\OCP\Contacts\IManager::class);
5020
-		$this->overwriteService(\OCP\Contacts\IManager::class, $cm);
5021
-
5022
-		$cm->method('search')
5023
-			->willReturnMap([
5024
-				['[email protected]', ['CLOUD'], [
5025
-					'limit' => 1,
5026
-					'enumeration' => false,
5027
-					'strict_search' => true,
5028
-				],
5029
-					[
5030
-						[
5031
-							'CLOUD' => [
5032
-								'[email protected]',
5033
-							],
5034
-							'FN' => 'foobar',
5035
-						],
5036
-					],
5037
-				],
5038
-				['[email protected]', ['EMAIL'], [
5039
-					'limit' => 1,
5040
-					'enumeration' => false,
5041
-					'strict_search' => true,
5042
-				],
5043
-					[
5044
-						[
5045
-							'EMAIL' => [
5046
-								'[email protected]',
5047
-							],
5048
-							'FN' => 'mail display name',
5049
-						],
5050
-					],
5051
-				],
5052
-			]);
5053
-
5054
-		try {
5055
-			$result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5056
-			$this->assertFalse($exception);
5057
-			$this->assertEquals($expects, $result);
5058
-		} catch (NotFoundException $e) {
5059
-			$this->assertTrue($exception);
5060
-		}
5061
-	}
5062
-
5063
-	public static function dataFormatRoomShare(): array {
5064
-		$result = [];
5065
-
5066
-		$result[] = [
5067
-			[
5068
-				'id' => '42',
5069
-				'share_type' => IShare::TYPE_ROOM,
5070
-				'uid_owner' => 'initiator',
5071
-				'displayname_owner' => 'initiator',
5072
-				'permissions' => 1,
5073
-				'stime' => 946684862,
5074
-				'parent' => null,
5075
-				'expiration' => null,
5076
-				'token' => null,
5077
-				'uid_file_owner' => 'owner',
5078
-				'displayname_file_owner' => 'owner',
5079
-				'note' => 'personal note',
5080
-				'path' => 'file',
5081
-				'item_type' => 'file',
5082
-				'storage_id' => 'storageId',
5083
-				'storage' => 100,
5084
-				'item_source' => 3,
5085
-				'file_source' => 3,
5086
-				'file_parent' => 1,
5087
-				'file_target' => 'myTarget',
5088
-				'share_with' => 'recipientRoom',
5089
-				'share_with_displayname' => '',
5090
-				'mail_send' => 0,
5091
-				'mimetype' => 'myMimeType',
5092
-				'has_preview' => false,
5093
-				'hide_download' => 0,
5094
-				'label' => '',
5095
-				'can_edit' => false,
5096
-				'can_delete' => false,
5097
-				'item_size' => 123456,
5098
-				'item_mtime' => 1234567890,
5099
-				'is-mount-root' => false,
5100
-				'mount-type' => '',
5101
-				'attributes' => null,
5102
-				'item_permissions' => 1,
5103
-			], false, []
5104
-		];
5105
-
5106
-		$result[] = [
5107
-			[
5108
-				'id' => '42',
5109
-				'share_type' => IShare::TYPE_ROOM,
5110
-				'uid_owner' => 'initiator',
5111
-				'displayname_owner' => 'initiator',
5112
-				'permissions' => 1,
5113
-				'stime' => 946684862,
5114
-				'parent' => null,
5115
-				'expiration' => null,
5116
-				'token' => null,
5117
-				'uid_file_owner' => 'owner',
5118
-				'displayname_file_owner' => 'owner',
5119
-				'note' => 'personal note',
5120
-				'path' => 'file',
5121
-				'item_type' => 'file',
5122
-				'storage_id' => 'storageId',
5123
-				'storage' => 100,
5124
-				'item_source' => 3,
5125
-				'file_source' => 3,
5126
-				'file_parent' => 1,
5127
-				'file_target' => 'myTarget',
5128
-				'share_with' => 'recipientRoom',
5129
-				'share_with_displayname' => 'recipientRoomName',
5130
-				'mail_send' => 0,
5131
-				'mimetype' => 'myMimeType',
5132
-				'has_preview' => false,
5133
-				'hide_download' => 0,
5134
-				'label' => '',
5135
-				'can_edit' => false,
5136
-				'can_delete' => false,
5137
-				'item_size' => 123456,
5138
-				'item_mtime' => 1234567890,
5139
-				'is-mount-root' => false,
5140
-				'mount-type' => '',
5141
-				'attributes' => null,
5142
-				'item_permissions' => 9,
5143
-			], true, [
5144
-				'share_with_displayname' => 'recipientRoomName'
5145
-			]
5146
-		];
5147
-
5148
-		return $result;
5149
-	}
5150
-
5151
-	/**
5152
-	 *
5153
-	 * @param array $expects
5154
-	 * @param IShare $share
5155
-	 * @param bool $helperAvailable
5156
-	 * @param array $formatShareByHelper
5157
-	 */
5158
-	#[DataProvider('dataFormatRoomShare')]
5159
-	public function testFormatRoomShare(array $expects, bool $helperAvailable, array $formatShareByHelper): void {
5160
-		$file = $this->createMock(File::class);
5161
-
5162
-		$file->method('getMimeType')->willReturn('myMimeType');
5163
-		$file->method('getPath')->willReturn('file');
5164
-		$file->method('getId')->willReturn(3);
5165
-
5166
-		$parent = $this->createMock(Folder::class);
5167
-		$parent->method('getId')->willReturn(1);
5168
-		$file->method('getParent')->willReturn($parent);
5169
-
5170
-		$file->method('getSize')->willReturn(123456);
5171
-		$file->method('getMTime')->willReturn(1234567890);
5172
-
5173
-		$mountPoint = $this->createMock(IMountPoint::class);
5174
-		$mountPoint->method('getMountType')->willReturn('');
5175
-		$file->method('getMountPoint')->willReturn($mountPoint);
5176
-
5177
-		$cache = $this->createMock(ICache::class);
5178
-		$cache->method('getNumericStorageId')->willReturn(100);
5179
-		$storage = $this->createMock(IStorage::class);
5180
-		$storage->method('getId')->willReturn('storageId');
5181
-		$storage->method('getCache')->willReturn($cache);
5182
-
5183
-		$file->method('getStorage')->willReturn($storage);
5184
-
5185
-		$share = Server::get(IManager::class)->newShare();
5186
-		$share->setShareType(IShare::TYPE_ROOM)
5187
-			->setSharedWith('recipientRoom')
5188
-			->setSharedBy('initiator')
5189
-			->setShareOwner('owner')
5190
-			->setPermissions(Constants::PERMISSION_READ)
5191
-			->setNode($file)
5192
-			->setShareTime(new \DateTime('2000-01-01T00:01:02'))
5193
-			->setTarget('myTarget')
5194
-			->setNote('personal note')
5195
-			->setId(42);
5196
-
5197
-		$this->rootFolder->method('getUserFolder')
5198
-			->with($this->currentUser)
5199
-			->willReturnSelf();
5200
-
5201
-		$this->rootFolder->method('getFirstNodeById')
5202
-			->with($share->getNodeId())
5203
-			->willReturn($share->getNode());
5204
-
5205
-		$this->rootFolder->method('getRelativePath')
5206
-			->with($share->getNode()->getPath())
5207
-			->willReturnArgument(0);
5208
-
5209
-		if (!$helperAvailable) {
5210
-			$this->appManager->method('isEnabledForUser')
5211
-				->with('spreed')
5212
-				->willReturn(false);
5213
-		} else {
5214
-			$this->appManager->method('isEnabledForUser')
5215
-				->with('spreed')
5216
-				->willReturn(true);
5217
-
5218
-			// This is not possible anymore with PHPUnit 10+
5219
-			// as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
5220
-			// $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
5221
-			$helper = $this->getMockBuilder(\stdClass::class)
5222
-				->addMethods(['formatShare', 'canAccessShare'])
5223
-				->getMock();
5224
-			$helper->method('formatShare')
5225
-				->with($share)
5226
-				->willReturn($formatShareByHelper);
5227
-			$helper->method('canAccessShare')
5228
-				->with($share)
5229
-				->willReturn(true);
5230
-
5231
-			$this->serverContainer->method('get')
5232
-				->with('\OCA\Talk\Share\Helper\ShareAPIController')
5233
-				->willReturn($helper);
5234
-		}
5235
-
5236
-		$result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5237
-		$this->assertEquals($expects, $result);
5238
-	}
5239
-
5240
-	/**
5241
-	 * @return list{Folder, Folder}
5242
-	 */
5243
-	private function getNonSharedUserFolder(): array {
5244
-		$node = $this->getMockBuilder(Folder::class)->getMock();
5245
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
5246
-		$storage = $this->createMock(IStorage::class);
5247
-		$storage->method('instanceOfStorage')
5248
-			->willReturnMap([
5249
-				['OCA\Files_Sharing\External\Storage', false],
5250
-				['OCA\Files_Sharing\SharedStorage', false],
5251
-			]);
5252
-		$userFolder->method('getStorage')->willReturn($storage);
5253
-		$node->method('getStorage')->willReturn($storage);
5254
-		$node->method('getId')->willReturn(42);
5255
-		$user = $this->createMock(IUser::class);
5256
-		$user->method('getUID')->willReturn($this->currentUser);
5257
-		$node->method('getOwner')->willReturn($user);
5258
-		return [$userFolder, $node];
5259
-	}
5260
-
5261
-	/**
5262
-	 * @return list{Folder, File}
5263
-	 */
5264
-	private function getNonSharedUserFile(): array {
5265
-		$node = $this->getMockBuilder(File::class)->getMock();
5266
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
5267
-		$storage = $this->createMock(IStorage::class);
5268
-		$storage->method('instanceOfStorage')
5269
-			->willReturnMap([
5270
-				['OCA\Files_Sharing\External\Storage', false],
5271
-				['OCA\Files_Sharing\SharedStorage', false],
5272
-			]);
5273
-		$userFolder->method('getStorage')->willReturn($storage);
5274
-		$node->method('getStorage')->willReturn($storage);
5275
-		$node->method('getId')->willReturn(42);
5276
-		return [$userFolder, $node];
5277
-	}
5278
-
5279
-	public function testPopulateTags(): void {
5280
-		$tagger = $this->createMock(ITags::class);
5281
-		$this->tagManager->method('load')
5282
-			->with('files')
5283
-			->willReturn($tagger);
5284
-		$data = [
5285
-			['file_source' => 10],
5286
-			['file_source' => 22, 'foo' => 'bar'],
5287
-			['file_source' => 42, 'x' => 'y'],
5288
-		];
5289
-		$tags = [
5290
-			10 => ['tag3'],
5291
-			42 => ['tag1', 'tag2'],
5292
-		];
5293
-		$tagger->method('getTagsForObjects')
5294
-			->with([10, 22, 42])
5295
-			->willReturn($tags);
5296
-
5297
-		$result = self::invokePrivate($this->ocs, 'populateTags', [$data]);
5298
-		$this->assertSame([
5299
-			['file_source' => 10, 'tags' => ['tag3']],
5300
-			['file_source' => 22, 'foo' => 'bar', 'tags' => []],
5301
-			['file_source' => 42, 'x' => 'y', 'tags' => ['tag1', 'tag2']],
5302
-		], $result);
5303
-	}
5304
-
5305
-	public static function trustedServerProvider(): array {
5306
-		return [
5307
-			'Trusted server' => [true, true],
5308
-			'Untrusted server' => [false, false],
5309
-		];
5310
-	}
5311
-
5312
-	#[DataProvider('trustedServerProvider')]
5313
-	public function testFormatShareWithFederatedShare(bool $isKnownServer, bool $isTrusted): void {
5314
-		$nodeId = 12;
5315
-		$nodePath = '/test.txt';
5316
-
5317
-		$node = $this->createMock(File::class);
5318
-		$node->method('getId')->willReturn($nodeId);
5319
-		$node->method('getPath')->willReturn($nodePath);
5320
-		$node->method('getInternalPath')->willReturn(ltrim($nodePath, '/'));
5321
-		$mountPoint = $this->createMock(IMountPoint::class);
5322
-		$mountPoint->method('getMountType')->willReturn('local');
5323
-		$node->method('getMountPoint')->willReturn($mountPoint);
5324
-		$node->method('getMimetype')->willReturn('text/plain');
5325
-		$storage = $this->createMock(IStorage::class);
5326
-		$storageCache = $this->createMock(ICache::class);
5327
-		$storageCache->method('getNumericStorageId')->willReturn(1);
5328
-		$storage->method('getCache')->willReturn($storageCache);
5329
-		$storage->method('getId')->willReturn('home::shareOwner');
5330
-		$node->method('getStorage')->willReturn($storage);
5331
-		$parent = $this->createMock(Folder::class);
5332
-		$parent->method('getId')->willReturn(2);
5333
-		$node->method('getParent')->willReturn($parent);
5334
-		$node->method('getSize')->willReturn(1234);
5335
-		$node->method('getMTime')->willReturn(1234567890);
5336
-
5337
-		$share = $this->createShare(
5338
-			1,
5339
-			IShare::TYPE_REMOTE,
5340
-			'[email protected]', // shared with
5341
-			'[email protected]',      // shared by
5342
-			'shareOwner',                 // share owner
5343
-			$node,
5344
-			Constants::PERMISSION_READ,
5345
-			time(),
5346
-			null,
5347
-			2,
5348
-			$nodePath,
5349
-			$nodeId
5350
-		);
5351
-
5352
-		$this->previewManager->method('isAvailable')->with($node)->willReturn(false);
5353
-
5354
-		$this->rootFolder->method('getUserFolder')
5355
-			->with($this->currentUser)
5356
-			->willReturnSelf();
5357
-
5358
-		$this->rootFolder->method('getFirstNodeById')
5359
-			->with($share->getNodeId())
5360
-			->willReturn($node);
5361
-
5362
-		$this->rootFolder->method('getRelativePath')
5363
-			->with($node->getPath())
5364
-			->willReturnArgument(0);
5365
-
5366
-		$serverName = 'remoteserver.com';
5367
-		$this->trustedServers->method('isTrustedServer')
5368
-			->with($serverName)
5369
-			->willReturn($isKnownServer);
5370
-
5371
-		$result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5372
-
5373
-		$this->assertSame($isTrusted, $result['is_trusted_server']);
5374
-	}
5375
-
5376
-	public function testFormatShareWithFederatedShareWithAtInUsername(): void {
5377
-		$nodeId = 12;
5378
-		$nodePath = '/test.txt';
5379
-
5380
-		$node = $this->createMock(File::class);
5381
-		$node->method('getId')->willReturn($nodeId);
5382
-		$node->method('getPath')->willReturn($nodePath);
5383
-		$node->method('getInternalPath')->willReturn(ltrim($nodePath, '/'));
5384
-		$mountPoint = $this->createMock(IMountPoint::class);
5385
-		$mountPoint->method('getMountType')->willReturn('local');
5386
-		$node->method('getMountPoint')->willReturn($mountPoint);
5387
-		$node->method('getMimetype')->willReturn('text/plain');
5388
-		$storage = $this->createMock(IStorage::class);
5389
-		$storageCache = $this->createMock(ICache::class);
5390
-		$storageCache->method('getNumericStorageId')->willReturn(1);
5391
-		$storage->method('getCache')->willReturn($storageCache);
5392
-		$storage->method('getId')->willReturn('home::shareOwner');
5393
-		$node->method('getStorage')->willReturn($storage);
5394
-		$parent = $this->createMock(Folder::class);
5395
-		$parent->method('getId')->willReturn(2);
5396
-		$node->method('getParent')->willReturn($parent);
5397
-		$node->method('getSize')->willReturn(1234);
5398
-		$node->method('getMTime')->willReturn(1234567890);
5399
-
5400
-		$share = $this->createShare(
5401
-			1,
5402
-			IShare::TYPE_REMOTE,
5403
-			'[email protected]@remoteserver.com',
5404
-			'[email protected]',
5405
-			'shareOwner',
5406
-			$node,
5407
-			Constants::PERMISSION_READ,
5408
-			time(),
5409
-			null,
5410
-			2,
5411
-			$nodePath,
5412
-			$nodeId
5413
-		);
5414
-
5415
-		$this->previewManager->method('isAvailable')->with($node)->willReturn(false);
5416
-
5417
-		$this->rootFolder->method('getUserFolder')
5418
-			->with($this->currentUser)
5419
-			->willReturnSelf();
5420
-
5421
-		$this->rootFolder->method('getFirstNodeById')
5422
-			->with($share->getNodeId())
5423
-			->willReturn($node);
5424
-
5425
-		$this->rootFolder->method('getRelativePath')
5426
-			->with($node->getPath())
5427
-			->willReturnArgument(0);
5428
-
5429
-		$serverName = 'remoteserver.com';
5430
-		$this->trustedServers->method('isTrustedServer')
5431
-			->with($serverName)
5432
-			->willReturn(true);
5433
-
5434
-		$result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5435
-
5436
-		$this->assertTrue($result['is_trusted_server']);
5437
-	}
5438
-
5439
-	public function testOwnerCanAlwaysDownload(): void {
5440
-		$ocs = $this->mockFormatShare();
5441
-
5442
-		$share = $this->createMock(IShare::class);
5443
-		$node = $this->createMock(File::class);
5444
-		$userFolder = $this->createMock(Folder::class);
5445
-		$owner = $this->createMock(IUser::class);
5446
-
5447
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5448
-		$share->method('getNodeId')->willReturn(42);
5449
-		$node->method('getOwner')->willReturn($owner);
5450
-		$owner->method('getUID')->willReturn('sharedByUser');
5451
-
5452
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5453
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5454
-
5455
-		// Expect hideDownload to be set to false since owner can always download
5456
-		$share->expects($this->once())->method('setHideDownload')->with(false);
5457
-
5458
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5459
-	}
5460
-
5461
-	public function testParentHideDownloadEnforcedOnChild(): void {
5462
-		$ocs = $this->mockFormatShare();
5463
-
5464
-		$share = $this->createMock(IShare::class);
5465
-		$node = $this->createMock(File::class);
5466
-		$userFolder = $this->createMock(Folder::class);
5467
-		$owner = $this->createMock(IUser::class);
5468
-		$storage = $this->createMock(SharedStorage::class);
5469
-		$originalShare = $this->createMock(IShare::class);
5470
-
5471
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5472
-		$share->method('getNodeId')->willReturn(42);
5473
-		$share->method('getHideDownload')->willReturn(false); // User wants to allow downloads
5474
-		$node->method('getOwner')->willReturn($owner);
5475
-		$owner->method('getUID')->willReturn('differentOwner');
5476
-		$node->method('getStorage')->willReturn($storage);
5477
-		$storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5478
-		$storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5479
-		$storage->method('getShare')->willReturn($originalShare);
5480
-		$originalShare->method('getHideDownload')->willReturn(true); // Parent hides download
5481
-		$originalShare->method('getAttributes')->willReturn(null);
5482
-
5483
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5484
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5485
-
5486
-		// Should be forced to hide download due to parent restriction
5487
-		$share->expects($this->once())->method('setHideDownload')->with(true);
5488
-
5489
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5490
-	}
5491
-
5492
-	public function testUserCanHideWhenParentAllows(): void {
5493
-		$ocs = $this->mockFormatShare();
5494
-
5495
-		$share = $this->createMock(IShare::class);
5496
-		$node = $this->createMock(File::class);
5497
-		$userFolder = $this->createMock(Folder::class);
5498
-		$owner = $this->createMock(IUser::class);
5499
-		$storage = $this->createMock(SharedStorage::class);
5500
-		$originalShare = $this->createMock(IShare::class);
5501
-
5502
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5503
-		$share->method('getNodeId')->willReturn(42);
5504
-		$share->method('getHideDownload')->willReturn(true); // User chooses to hide downloads
5505
-		$node->method('getOwner')->willReturn($owner);
5506
-		$owner->method('getUID')->willReturn('differentOwner');
5507
-		$node->method('getStorage')->willReturn($storage);
5508
-		$storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5509
-		$storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5510
-		$storage->method('getShare')->willReturn($originalShare);
5511
-		$originalShare->method('getHideDownload')->willReturn(false); // Parent allows download
5512
-		$originalShare->method('getAttributes')->willReturn(null);
5513
-
5514
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5515
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5516
-
5517
-		// Should respect user's choice to hide downloads
5518
-		$share->expects($this->once())->method('setHideDownload')->with(true);
5519
-
5520
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5521
-	}
5522
-
5523
-	public function testParentDownloadAttributeInherited(): void {
5524
-		$ocs = $this->mockFormatShare();
5525
-
5526
-		$share = $this->createMock(IShare::class);
5527
-		$node = $this->createMock(File::class);
5528
-		$userFolder = $this->createMock(Folder::class);
5529
-		$owner = $this->createMock(IUser::class);
5530
-		$storage = $this->createMock(SharedStorage::class);
5531
-		$originalShare = $this->createMock(IShare::class);
5532
-		$attributes = $this->createMock(IShareAttributes::class);
5533
-		$shareAttributes = $this->createMock(IShareAttributes::class);
5534
-
5535
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5536
-		$share->method('getNodeId')->willReturn(42);
5537
-		$share->method('getHideDownload')->willReturn(false); // User wants to allow downloads
5538
-		$share->method('getAttributes')->willReturn($shareAttributes);
5539
-		$share->method('newAttributes')->willReturn($shareAttributes);
5540
-		$node->method('getOwner')->willReturn($owner);
5541
-		$owner->method('getUID')->willReturn('differentOwner');
5542
-		$node->method('getStorage')->willReturn($storage);
5543
-		$storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5544
-		$storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5545
-		$storage->method('getShare')->willReturn($originalShare);
5546
-		$originalShare->method('getHideDownload')->willReturn(false);
5547
-		$originalShare->method('getAttributes')->willReturn($attributes);
5548
-		$attributes->method('getAttribute')->with('permissions', 'download')->willReturn(false); // Parent forbids download
5549
-
5550
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5551
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5552
-
5553
-		// Should be forced to hide download and set download attribute to false
5554
-		$share->expects($this->once())->method('setHideDownload')->with(true);
5555
-		$shareAttributes->expects($this->once())->method('setAttribute')->with('permissions', 'download', false);
5556
-		$share->expects($this->once())->method('setAttributes')->with($shareAttributes);
5557
-
5558
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5559
-	}
5560
-
5561
-	public function testFederatedStorageRespectsUserChoice(): void {
5562
-		$ocs = $this->mockFormatShare();
5563
-
5564
-		$share = $this->createMock(IShare::class);
5565
-		$node = $this->createMock(File::class);
5566
-		$userFolder = $this->createMock(Folder::class);
5567
-		$owner = $this->createMock(IUser::class);
5568
-		$storage = $this->createMock(Storage::class);
5569
-
5570
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5571
-		$share->method('getNodeId')->willReturn(42);
5572
-		$share->method('getHideDownload')->willReturn(true); // User chooses to hide downloads
5573
-		$node->method('getOwner')->willReturn($owner);
5574
-		$owner->method('getUID')->willReturn('differentOwner');
5575
-		$node->method('getStorage')->willReturn($storage);
5576
-		$storage->method('instanceOfStorage')->willReturnMap([
5577
-			[SharedStorage::class, false],
5578
-			[Storage::class, true]
5579
-		]);
5580
-
5581
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5582
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5583
-
5584
-		// For federated storage, should respect user's choice
5585
-		$share->expects($this->once())->method('setHideDownload')->with(true);
5586
-
5587
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5588
-	}
5589
-
5590
-	public function testUserAllowsDownloadWhenParentPermits(): void {
5591
-		$ocs = $this->mockFormatShare();
5592
-
5593
-		$share = $this->createMock(IShare::class);
5594
-		$node = $this->createMock(File::class);
5595
-		$userFolder = $this->createMock(Folder::class);
5596
-		$owner = $this->createMock(IUser::class);
5597
-		$storage = $this->createMock(SharedStorage::class);
5598
-		$originalShare = $this->createMock(IShare::class);
5599
-
5600
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5601
-		$share->method('getNodeId')->willReturn(42);
5602
-		$share->method('getHideDownload')->willReturn(false); // User wants to allow downloads
5603
-		$node->method('getOwner')->willReturn($owner);
5604
-		$owner->method('getUID')->willReturn('differentOwner');
5605
-		$node->method('getStorage')->willReturn($storage);
5606
-		$storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5607
-		$storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5608
-		$storage->method('getShare')->willReturn($originalShare);
5609
-		$originalShare->method('getHideDownload')->willReturn(false); // Parent allows download
5610
-		$originalShare->method('getAttributes')->willReturn(null);
5611
-
5612
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5613
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5614
-
5615
-		// Should allow downloads as both user and parent permit it
5616
-		$share->expects($this->once())->method('setHideDownload')->with(false);
5617
-
5618
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5619
-	}
5620
-
5621
-	public function testWrapperStorageUnwrapped(): void {
5622
-		$ocs = $this->mockFormatShare();
5623
-
5624
-		$share = $this->createMock(IShare::class);
5625
-		$node = $this->createMock(File::class);
5626
-		$userFolder = $this->createMock(Folder::class);
5627
-		$owner = $this->createMock(IUser::class);
5628
-		$wrapperStorage = $this->createMock(Wrapper::class);
5629
-		$innerStorage = $this->createMock(SharedStorage::class);
5630
-		$originalShare = $this->createMock(IShare::class);
5631
-
5632
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5633
-		$share->method('getNodeId')->willReturn(42);
5634
-		$share->method('getHideDownload')->willReturn(false);
5635
-		$node->method('getOwner')->willReturn($owner);
5636
-		$owner->method('getUID')->willReturn('differentOwner');
5637
-		$node->method('getStorage')->willReturn($wrapperStorage);
5638
-		$wrapperStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5639
-		$wrapperStorage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($innerStorage);
5640
-		$innerStorage->method('getShare')->willReturn($originalShare);
5641
-		$originalShare->method('getHideDownload')->willReturn(false);
5642
-		$originalShare->method('getAttributes')->willReturn(null);
5643
-
5644
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5645
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5646
-
5647
-		$share->expects($this->once())->method('setHideDownload')->with(false);
5648
-
5649
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5650
-	}
594
+    public function createShare(
595
+        int $id,
596
+        int $shareType,
597
+        ?string $sharedWith,
598
+        string $sharedBy,
599
+        string $shareOwner,
600
+        File|Folder|null $node,
601
+        int $permissions,
602
+        int $shareTime,
603
+        ?\DateTime $expiration,
604
+        int $parent,
605
+        string $target,
606
+        int $mail_send,
607
+        string $note = '',
608
+        ?string $token = null,
609
+        ?string $password = null,
610
+        string $label = '',
611
+        ?IShareAttributes $attributes = null,
612
+    ): MockObject {
613
+        $share = $this->createMock(IShare::class);
614
+        $share->method('getId')->willReturn($id);
615
+        $share->method('getShareType')->willReturn($shareType);
616
+        $share->method('getSharedWith')->willReturn($sharedWith);
617
+        $share->method('getSharedBy')->willReturn($sharedBy);
618
+        $share->method('getShareOwner')->willReturn($shareOwner);
619
+        $share->method('getNode')->willReturn($node);
620
+        $share->method('getPermissions')->willReturn($permissions);
621
+        $share->method('getNote')->willReturn($note);
622
+        $share->method('getLabel')->willReturn($label);
623
+        $share->method('getAttributes')->willReturn($attributes);
624
+        $time = new \DateTime();
625
+        $time->setTimestamp($shareTime);
626
+        $share->method('getShareTime')->willReturn($time);
627
+        $share->method('getExpirationDate')->willReturn($expiration);
628
+        $share->method('getTarget')->willReturn($target);
629
+        $share->method('getMailSend')->willReturn($mail_send);
630
+        $share->method('getToken')->willReturn($token);
631
+        $share->method('getPassword')->willReturn($password);
632
+
633
+        if ($shareType === IShare::TYPE_USER
634
+            || $shareType === IShare::TYPE_GROUP
635
+            || $shareType === IShare::TYPE_LINK) {
636
+            $share->method('getFullId')->willReturn('ocinternal:' . $id);
637
+        }
638
+
639
+        return $share;
640
+    }
641
+
642
+    public static function dataGetShare(): array {
643
+        $data = [];
644
+
645
+        $file = [
646
+            'class' => File::class,
647
+            'id' => 1,
648
+            'path' => 'file',
649
+            'mimeType' => 'myMimeType',
650
+        ];
651
+
652
+        $folder = [
653
+            'class' => Folder::class,
654
+            'id' => 2,
655
+            'path' => 'folder',
656
+            'mimeType' => 'myFolderMimeType',
657
+        ];
658
+
659
+        // File shared with user
660
+        $share = [
661
+            100,
662
+            IShare::TYPE_USER,
663
+            'userId',
664
+            'initiatorId',
665
+            'ownerId',
666
+            $file,
667
+            4,
668
+            5,
669
+            null,
670
+            6,
671
+            'target',
672
+            0,
673
+            'personal note',
674
+            null,
675
+            null,
676
+            '',
677
+            [],
678
+        ];
679
+        $expected = [
680
+            'id' => 100,
681
+            'share_type' => IShare::TYPE_USER,
682
+            'share_with' => 'userId',
683
+            'share_with_displayname' => 'userDisplay',
684
+            'share_with_displayname_unique' => '[email protected]',
685
+            'uid_owner' => 'initiatorId',
686
+            'displayname_owner' => 'initiatorDisplay',
687
+            'item_type' => 'file',
688
+            'item_source' => 1,
689
+            'file_source' => 1,
690
+            'file_target' => 'target',
691
+            'file_parent' => 3,
692
+            'token' => null,
693
+            'expiration' => null,
694
+            'permissions' => 4,
695
+            'stime' => 5,
696
+            'parent' => null,
697
+            'storage_id' => 'STORAGE',
698
+            'path' => 'file',
699
+            'storage' => 101,
700
+            'mail_send' => 0,
701
+            'uid_file_owner' => 'ownerId',
702
+            'note' => 'personal note',
703
+            'label' => '',
704
+            'displayname_file_owner' => 'ownerDisplay',
705
+            'mimetype' => 'myMimeType',
706
+            'has_preview' => false,
707
+            'hide_download' => 0,
708
+            'can_edit' => false,
709
+            'can_delete' => false,
710
+            'item_size' => 123465,
711
+            'item_mtime' => 1234567890,
712
+            'item_permissions' => 4,
713
+            'is-mount-root' => false,
714
+            'mount-type' => '',
715
+        ];
716
+        $data['File shared with user'] = [$share, $expected, true];
717
+
718
+        // Folder shared with group
719
+        $share = [
720
+            101,
721
+            IShare::TYPE_GROUP,
722
+            'groupId',
723
+            'initiatorId',
724
+            'ownerId',
725
+            $folder,
726
+            4,
727
+            5,
728
+            null,
729
+            6,
730
+            'target',
731
+            0,
732
+            'personal note',
733
+            null,
734
+            null,
735
+            '',
736
+            [],
737
+        ];
738
+        $expected = [
739
+            'id' => 101,
740
+            'share_type' => IShare::TYPE_GROUP,
741
+            'share_with' => 'groupId',
742
+            'share_with_displayname' => 'groupId',
743
+            'uid_owner' => 'initiatorId',
744
+            'displayname_owner' => 'initiatorDisplay',
745
+            'item_type' => 'folder',
746
+            'item_source' => 2,
747
+            'file_source' => 2,
748
+            'file_target' => 'target',
749
+            'file_parent' => 3,
750
+            'token' => null,
751
+            'expiration' => null,
752
+            'permissions' => 4,
753
+            'stime' => 5,
754
+            'parent' => null,
755
+            'storage_id' => 'STORAGE',
756
+            'path' => 'folder',
757
+            'storage' => 101,
758
+            'mail_send' => 0,
759
+            'uid_file_owner' => 'ownerId',
760
+            'note' => 'personal note',
761
+            'label' => '',
762
+            'displayname_file_owner' => 'ownerDisplay',
763
+            'mimetype' => 'myFolderMimeType',
764
+            'has_preview' => false,
765
+            'hide_download' => 0,
766
+            'can_edit' => false,
767
+            'can_delete' => false,
768
+            'item_size' => 123465,
769
+            'item_mtime' => 1234567890,
770
+            'item_permissions' => 4,
771
+            'is-mount-root' => false,
772
+            'mount-type' => '',
773
+        ];
774
+        $data['Folder shared with group'] = [$share, $expected, true];
775
+
776
+        // File shared by link with Expire
777
+        $expire = \DateTime::createFromFormat('Y-m-d h:i:s', '2000-01-02 01:02:03');
778
+        $share = [
779
+            101,
780
+            IShare::TYPE_LINK,
781
+            null,
782
+            'initiatorId',
783
+            'ownerId',
784
+            $folder,
785
+            4,
786
+            5,
787
+            $expire,
788
+            6,
789
+            'target',
790
+            0,
791
+            'personal note',
792
+            'token',
793
+            'password',
794
+            'first link share'
795
+        ];
796
+        $expected = [
797
+            'id' => 101,
798
+            'share_type' => IShare::TYPE_LINK,
799
+            'password' => 'password',
800
+            'share_with' => 'password',
801
+            'share_with_displayname' => '(Shared link)',
802
+            'send_password_by_talk' => false,
803
+            'uid_owner' => 'initiatorId',
804
+            'displayname_owner' => 'initiatorDisplay',
805
+            'item_type' => 'folder',
806
+            'item_source' => 2,
807
+            'file_source' => 2,
808
+            'file_target' => 'target',
809
+            'file_parent' => 3,
810
+            'token' => 'token',
811
+            'expiration' => '2000-01-02 00:00:00',
812
+            'permissions' => 4,
813
+            'attributes' => null,
814
+            'stime' => 5,
815
+            'parent' => null,
816
+            'storage_id' => 'STORAGE',
817
+            'path' => 'folder',
818
+            'storage' => 101,
819
+            'mail_send' => 0,
820
+            'url' => 'url',
821
+            'uid_file_owner' => 'ownerId',
822
+            'note' => 'personal note',
823
+            'label' => 'first link share',
824
+            'displayname_file_owner' => 'ownerDisplay',
825
+            'mimetype' => 'myFolderMimeType',
826
+            'has_preview' => false,
827
+            'hide_download' => 0,
828
+            'can_edit' => false,
829
+            'can_delete' => false,
830
+            'item_size' => 123465,
831
+            'item_mtime' => 1234567890,
832
+            'item_permissions' => 4,
833
+            'is-mount-root' => false,
834
+            'mount-type' => '',
835
+        ];
836
+        $data['File shared by link with Expire'] = [$share, $expected, false];
837
+
838
+        return $data;
839
+    }
840
+
841
+    #[DataProvider('dataGetShare')]
842
+    public function testGetShare(array $shareParams, array $result, bool $attributes): void {
843
+
844
+        $cache = $this->createMock(ICache::class);
845
+        $cache->method('getNumericStorageId')->willReturn(101);
846
+
847
+        $storage = $this->createMock(IStorage::class);
848
+        $storage->method('getId')->willReturn('STORAGE');
849
+        $storage->method('getCache')->willReturn($cache);
850
+
851
+        $parentFolder = $this->createMock(Folder::class);
852
+        $parentFolder->method('getId')->willReturn(3);
853
+
854
+        $mountPoint = $this->createMock(IMountPoint::class);
855
+        $mountPoint->method('getMountType')->willReturn('');
856
+
857
+        $nodeParams = $shareParams[5];
858
+        $node = $this->createMock($nodeParams['class']);
859
+        $node->method('getId')->willReturn($nodeParams['id']);
860
+        $node->method('getPath')->willReturn($nodeParams['path']);
861
+        $node->method('getStorage')->willReturn($storage);
862
+        $node->method('getParent')->willReturn($parentFolder);
863
+        $node->method('getSize')->willReturn(123465);
864
+        $node->method('getMTime')->willReturn(1234567890);
865
+        $node->method('getMimeType')->willReturn($nodeParams['mimeType']);
866
+        $node->method('getMountPoint')->willReturn($mountPoint);
867
+
868
+        $shareParams[5] = $node;
869
+
870
+        if ($attributes) {
871
+            [$shareAttributes, $shareAttributesReturnJson] = $this->mockShareAttributes();
872
+            $result['attributes'] = $shareAttributesReturnJson;
873
+            $shareParams[16] = $shareAttributes;
874
+        }
875
+
876
+        $share = $this->createShare(...$shareParams);
877
+        /** @var ShareAPIController&MockObject $ocs */
878
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
879
+            ->setConstructorArgs([
880
+                $this->appName,
881
+                $this->request,
882
+                $this->shareManager,
883
+                $this->groupManager,
884
+                $this->userManager,
885
+                $this->rootFolder,
886
+                $this->urlGenerator,
887
+                $this->l,
888
+                $this->config,
889
+                $this->appConfig,
890
+                $this->appManager,
891
+                $this->serverContainer,
892
+                $this->userStatusManager,
893
+                $this->previewManager,
894
+                $this->dateTimeZone,
895
+                $this->logger,
896
+                $this->factory,
897
+                $this->mailer,
898
+                $this->tagManager,
899
+                $this->getEmailValidatorWithStrictEmailCheck(),
900
+                $this->trustedServers,
901
+                $this->currentUser,
902
+            ])
903
+            ->onlyMethods(['canAccessShare'])
904
+            ->getMock();
905
+
906
+        $ocs->expects($this->any())
907
+            ->method('canAccessShare')
908
+            ->willReturn(true);
909
+
910
+        $this->shareManager
911
+            ->expects($this->any())
912
+            ->method('getShareById')
913
+            ->with($share->getFullId(), 'currentUser')
914
+            ->willReturn($share);
915
+
916
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
917
+        $userFolder
918
+            ->method('getRelativePath')
919
+            ->willReturnArgument(0);
920
+
921
+        $userFolder->method('getById')
922
+            ->with($share->getNodeId())
923
+            ->willReturn([$share->getNode()]);
924
+        $userFolder->method('getFirstNodeById')
925
+            ->with($share->getNodeId())
926
+            ->willReturn($share->getNode());
927
+
928
+        $this->rootFolder->method('getUserFolder')
929
+            ->with($this->currentUser)
930
+            ->willReturn($userFolder);
931
+
932
+        $this->urlGenerator
933
+            ->method('linkToRouteAbsolute')
934
+            ->willReturn('url');
935
+
936
+        $initiator = $this->getMockBuilder(IUser::class)->getMock();
937
+        $initiator->method('getUID')->willReturn('initiatorId');
938
+        $initiator->method('getDisplayName')->willReturn('initiatorDisplay');
939
+
940
+        $owner = $this->getMockBuilder(IUser::class)->getMock();
941
+        $owner->method('getUID')->willReturn('ownerId');
942
+        $owner->method('getDisplayName')->willReturn('ownerDisplay');
943
+
944
+        $user = $this->getMockBuilder(IUser::class)->getMock();
945
+        $user->method('getUID')->willReturn('userId');
946
+        $user->method('getDisplayName')->willReturn('userDisplay');
947
+        $user->method('getSystemEMailAddress')->willReturn('[email protected]');
948
+
949
+        $group = $this->getMockBuilder(IGroup::class)->getMock();
950
+        $group->method('getGID')->willReturn('groupId');
951
+
952
+        $this->userManager->method('get')->willReturnMap([
953
+            ['userId', $user],
954
+            ['initiatorId', $initiator],
955
+            ['ownerId', $owner],
956
+        ]);
957
+        $this->groupManager->method('get')->willReturnMap([
958
+            ['group', $group],
959
+        ]);
960
+        $this->dateTimeZone->method('getTimezone')->willReturn(new \DateTimeZone('UTC'));
961
+
962
+        $data = $ocs->getShare((string)$share->getId())->getData()[0];
963
+        $this->assertEquals($result, $data);
964
+    }
965
+
966
+
967
+    public function testGetShareInvalidNode(): void {
968
+        $this->expectException(OCSNotFoundException::class);
969
+        $this->expectExceptionMessage('Wrong share ID, share does not exist');
970
+
971
+        $share = Server::get(IManager::class)->newShare();
972
+        $share->setSharedBy('initiator')
973
+            ->setSharedWith('recipient')
974
+            ->setShareOwner('owner');
975
+
976
+        $this->shareManager
977
+            ->expects($this->once())
978
+            ->method('getShareById')
979
+            ->with('ocinternal:42', 'currentUser')
980
+            ->willReturn($share);
981
+
982
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
983
+        $this->rootFolder->method('getUserFolder')
984
+            ->with($this->currentUser)
985
+            ->willReturn($userFolder);
986
+
987
+        $this->ocs->getShare('42');
988
+    }
989
+
990
+    public static function dataGetShares(): array {
991
+        $file1 = [
992
+            'class' => File::class,
993
+            'methods' => [
994
+                'getName' => 'file1',
995
+            ]
996
+        ];
997
+        $file2 = [
998
+            'class' => File::class,
999
+            'methods' => [
1000
+                'getName' => 'file2',
1001
+            ]
1002
+        ];
1003
+
1004
+        $folder = [
1005
+            'class' => Folder::class,
1006
+            'methods' => [
1007
+                'getDirectoryListing' => [$file1, $file2]
1008
+            ]
1009
+        ];
1010
+
1011
+        $file1UserShareOwner = [
1012
+            'type' => IShare::TYPE_USER,
1013
+            'sharedWith' => 'recipient',
1014
+            'sharedBy' => 'initiator',
1015
+            'owner' => 'currentUser',
1016
+            'node' => $file1,
1017
+            'id' => 4,
1018
+        ];
1019
+
1020
+        $file1UserShareOwnerExpected = [
1021
+            'id' => 4,
1022
+            'share_type' => IShare::TYPE_USER,
1023
+        ];
1024
+
1025
+        $file1UserShareInitiator = [
1026
+            'type' => IShare::TYPE_USER,
1027
+            'sharedWith' => 'recipient',
1028
+            'sharedBy' => 'currentUser',
1029
+            'owner' => 'owner',
1030
+            'node' => $file1,
1031
+            'id' => 8,
1032
+        ];
1033
+
1034
+        $file1UserShareInitiatorExpected = [
1035
+            'id' => 8,
1036
+            'share_type' => IShare::TYPE_USER,
1037
+        ];
1038
+
1039
+        $file1UserShareRecipient = [
1040
+            'type' => IShare::TYPE_USER,
1041
+            'sharedWith' => 'currentUser',
1042
+            'sharedBy' => 'initiator',
1043
+            'owner' => 'owner',
1044
+            'node' => $file1,
1045
+            'id' => 15,
1046
+        ];
1047
+
1048
+        $file1UserShareRecipientExpected = [
1049
+            'id' => 15,
1050
+            'share_type' => IShare::TYPE_USER,
1051
+        ];
1052
+
1053
+        $file1UserShareOther = [
1054
+            'type' => IShare::TYPE_USER,
1055
+            'sharedWith' => 'recipient',
1056
+            'sharedBy' => 'initiator',
1057
+            'owner' => 'owner',
1058
+            'node' => $file1,
1059
+            'id' => 16,
1060
+        ];
1061
+
1062
+        $file1UserShareOtherExpected = [
1063
+            'id' => 16,
1064
+            'share_type' => IShare::TYPE_USER,
1065
+        ];
1066
+
1067
+        $file1GroupShareOwner = [
1068
+            'type' => IShare::TYPE_GROUP,
1069
+            'sharedWith' => 'recipient',
1070
+            'sharedBy' => 'initiator',
1071
+            'owner' => 'currentUser',
1072
+            'node' => $file1,
1073
+            'id' => 23,
1074
+        ];
1075
+
1076
+        $file1GroupShareOwnerExpected = [
1077
+            'id' => 23,
1078
+            'share_type' => IShare::TYPE_GROUP,
1079
+        ];
1080
+
1081
+        $file1GroupShareRecipient = [
1082
+            'type' => IShare::TYPE_GROUP,
1083
+            'sharedWith' => 'currentUserGroup',
1084
+            'sharedBy' => 'initiator',
1085
+            'owner' => 'owner',
1086
+            'node' => $file1,
1087
+            'id' => 42,
1088
+        ];
1089
+
1090
+        $file1GroupShareRecipientExpected = [
1091
+            'id' => 42,
1092
+            'share_type' => IShare::TYPE_GROUP,
1093
+        ];
1094
+
1095
+        $file1GroupShareOther = [
1096
+            'type' => IShare::TYPE_GROUP,
1097
+            'sharedWith' => 'recipient',
1098
+            'sharedBy' => 'initiator',
1099
+            'owner' => 'owner',
1100
+            'node' => $file1,
1101
+            'id' => 108,
1102
+        ];
1103
+
1104
+        $file1LinkShareOwner = [
1105
+            'type' => IShare::TYPE_LINK,
1106
+            'sharedWith' => 'recipient',
1107
+            'sharedBy' => 'initiator',
1108
+            'owner' => 'currentUser',
1109
+            'node' => $file1,
1110
+            'id' => 415,
1111
+        ];
1112
+
1113
+        $file1LinkShareOwnerExpected = [
1114
+            'id' => 415,
1115
+            'share_type' => IShare::TYPE_LINK,
1116
+        ];
1117
+
1118
+        $file1EmailShareOwner = [
1119
+            'type' => IShare::TYPE_EMAIL,
1120
+            'sharedWith' => 'recipient',
1121
+            'sharedBy' => 'initiator',
1122
+            'owner' => 'currentUser',
1123
+            'node' => $file1,
1124
+            'id' => 416,
1125
+        ];
1126
+
1127
+        $file1EmailShareOwnerExpected = [
1128
+            'id' => 416,
1129
+            'share_type' => IShare::TYPE_EMAIL,
1130
+        ];
1131
+
1132
+        $file1CircleShareOwner = [
1133
+            'type' => IShare::TYPE_CIRCLE,
1134
+            'sharedWith' => 'recipient',
1135
+            'sharedBy' => 'initiator',
1136
+            'owner' => 'currentUser',
1137
+            'node' => $file1,
1138
+            'id' => 423,
1139
+        ];
1140
+
1141
+        $file1CircleShareOwnerExpected = [
1142
+            'id' => 423,
1143
+            'share_type' => IShare::TYPE_CIRCLE,
1144
+        ];
1145
+
1146
+        $file1RoomShareOwner = [
1147
+            'type' => IShare::TYPE_ROOM,
1148
+            'sharedWith' => 'recipient',
1149
+            'sharedBy' => 'initiator',
1150
+            'owner' => 'currentUser',
1151
+            'node' => $file1,
1152
+            'id' => 442,
1153
+        ];
1154
+
1155
+        $file1RoomShareOwnerExpected = [
1156
+            'id' => 442,
1157
+            'share_type' => IShare::TYPE_ROOM,
1158
+        ];
1159
+
1160
+        $file1RemoteShareOwner = [
1161
+            'type' => IShare::TYPE_REMOTE,
1162
+            'sharedWith' => 'recipient',
1163
+            'sharedBy' => 'initiator',
1164
+            'owner' => 'currentUser',
1165
+            'expirationDate' => new \DateTime('2000-01-01T01:02:03'),
1166
+            'node' => $file1,
1167
+            'id' => 815,
1168
+        ];
1169
+
1170
+        $file1RemoteShareOwnerExpected = [
1171
+            'id' => 815,
1172
+            'share_type' => IShare::TYPE_REMOTE,
1173
+        ];
1174
+
1175
+        $file1RemoteGroupShareOwner = [
1176
+            'type' => IShare::TYPE_REMOTE_GROUP,
1177
+            'sharedWith' => 'recipient',
1178
+            'sharedBy' => 'initiator',
1179
+            'owner' => 'currentUser',
1180
+            'expirationDate' => new \DateTime('2000-01-01T01:02:03'),
1181
+            'node' => $file1,
1182
+            'id' => 816,
1183
+        ];
1184
+
1185
+        $file1RemoteGroupShareOwnerExpected = [
1186
+            'id' => 816,
1187
+            'share_type' => IShare::TYPE_REMOTE_GROUP,
1188
+        ];
1189
+
1190
+        $file2UserShareOwner = [
1191
+            'type' => IShare::TYPE_USER,
1192
+            'sharedWith' => 'recipient',
1193
+            'sharedBy' => 'initiator',
1194
+            'owner' => 'currentUser',
1195
+            'node' => $file2,
1196
+            'id' => 823,
1197
+        ];
1198
+
1199
+        $file2UserShareOwnerExpected = [
1200
+            'id' => 823,
1201
+            'share_type' => IShare::TYPE_USER,
1202
+        ];
1203
+
1204
+        $data = [
1205
+            [
1206
+                [
1207
+                    'node' => $file1,
1208
+                ],
1209
+                [
1210
+                    'file1' => [
1211
+                        IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareOwner, $file1UserShareOwner],
1212
+                    ],
1213
+                ],
1214
+                [
1215
+                ],
1216
+                [
1217
+                    $file1UserShareOwnerExpected
1218
+                ]
1219
+            ],
1220
+            [
1221
+                [
1222
+                    'node' => $file1,
1223
+                ],
1224
+                [
1225
+                    'file1' => [
1226
+                        IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareRecipient],
1227
+                    ],
1228
+                ],
1229
+                [
1230
+                ],
1231
+                [
1232
+                    $file1UserShareOwnerExpected,
1233
+                ]
1234
+            ],
1235
+            [
1236
+                [
1237
+                    'node' => $file1,
1238
+                ],
1239
+                [
1240
+                    'file1' => [
1241
+                        IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1242
+                    ],
1243
+                ],
1244
+                [
1245
+                ],
1246
+                [
1247
+                    $file1UserShareOwnerExpected,
1248
+                    $file1UserShareInitiatorExpected,
1249
+                    $file1UserShareOtherExpected,
1250
+                ]
1251
+            ],
1252
+            [
1253
+                [
1254
+                    'node' => $file1,
1255
+                ],
1256
+                [
1257
+                    'file1' => [
1258
+                        IShare::TYPE_USER => [$file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1259
+                    ],
1260
+                ],
1261
+                [
1262
+                ],
1263
+                [
1264
+                    $file1UserShareInitiatorExpected,
1265
+                ]
1266
+            ],
1267
+            [
1268
+                [
1269
+                    'node' => $file1,
1270
+                ],
1271
+                [
1272
+                    'file1' => [
1273
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1274
+                        IShare::TYPE_GROUP => [$file1GroupShareRecipient],
1275
+                    ],
1276
+                ],
1277
+                [
1278
+                ],
1279
+                [
1280
+                    $file1UserShareOwnerExpected,
1281
+                    $file1GroupShareRecipientExpected,
1282
+                ]
1283
+            ],
1284
+            [
1285
+                [
1286
+                    'node' => $file1,
1287
+                ],
1288
+                [
1289
+                    'file1' => [
1290
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1291
+                        IShare::TYPE_GROUP => [$file1GroupShareOwner],
1292
+                        IShare::TYPE_LINK => [$file1LinkShareOwner],
1293
+                        IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1294
+                        IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1295
+                        IShare::TYPE_ROOM => [$file1RoomShareOwner],
1296
+                        IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1297
+                        IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1298
+                    ],
1299
+                ],
1300
+                [
1301
+                ],
1302
+                [
1303
+                    $file1UserShareOwnerExpected,
1304
+                    $file1GroupShareOwnerExpected,
1305
+                    $file1LinkShareOwnerExpected,
1306
+                    $file1EmailShareOwnerExpected,
1307
+                    $file1CircleShareOwnerExpected,
1308
+                    $file1RoomShareOwnerExpected,
1309
+                ]
1310
+            ],
1311
+            [
1312
+                [
1313
+                    'node' => $file1,
1314
+                ],
1315
+                [
1316
+                    'file1' => [
1317
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1318
+                        IShare::TYPE_GROUP => [$file1GroupShareOwner],
1319
+                        IShare::TYPE_LINK => [$file1LinkShareOwner],
1320
+                        IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1321
+                        IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1322
+                        IShare::TYPE_ROOM => [$file1RoomShareOwner],
1323
+                        IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1324
+                        IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1325
+                    ],
1326
+                ],
1327
+                [
1328
+                    IShare::TYPE_REMOTE => true,
1329
+                    IShare::TYPE_REMOTE_GROUP => true,
1330
+                ],
1331
+                [
1332
+                    $file1UserShareOwnerExpected,
1333
+                    $file1GroupShareOwnerExpected,
1334
+                    $file1LinkShareOwnerExpected,
1335
+                    $file1EmailShareOwnerExpected,
1336
+                    $file1CircleShareOwnerExpected,
1337
+                    $file1RoomShareOwnerExpected,
1338
+                    $file1RemoteShareOwnerExpected,
1339
+                    $file1RemoteGroupShareOwnerExpected,
1340
+                ]
1341
+            ],
1342
+            [
1343
+                [
1344
+                    'node' => $folder,
1345
+                    'subfiles' => 'true',
1346
+                ],
1347
+                [
1348
+                    'file1' => [
1349
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1350
+                    ],
1351
+                    'file2' => [
1352
+                        IShare::TYPE_USER => [$file2UserShareOwner],
1353
+                    ],
1354
+                ],
1355
+                [
1356
+                ],
1357
+                [
1358
+                    $file1UserShareOwnerExpected,
1359
+                    $file2UserShareOwnerExpected,
1360
+                ]
1361
+            ],
1362
+            [
1363
+                [
1364
+                    'node' => $folder,
1365
+                    'subfiles' => 'true',
1366
+                ],
1367
+                [
1368
+                    'file1' => [
1369
+                        IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareOwner, $file1UserShareOwner],
1370
+                    ],
1371
+                ],
1372
+                [
1373
+                ],
1374
+                [
1375
+                    $file1UserShareOwnerExpected,
1376
+                ]
1377
+            ],
1378
+            [
1379
+                [
1380
+                    'node' => $folder,
1381
+                    'subfiles' => 'true',
1382
+                ],
1383
+                [
1384
+                    'file1' => [
1385
+                        IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareRecipient],
1386
+                    ],
1387
+                ],
1388
+                [
1389
+                ],
1390
+                [
1391
+                    $file1UserShareOwnerExpected
1392
+                ]
1393
+            ],
1394
+            [
1395
+                [
1396
+                    'node' => $folder,
1397
+                    'subfiles' => 'true',
1398
+                ],
1399
+                [
1400
+                    'file1' => [
1401
+                        IShare::TYPE_USER => [$file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1402
+                    ],
1403
+                    'file2' => [
1404
+                        IShare::TYPE_USER => [$file2UserShareOwner],
1405
+                    ],
1406
+                ],
1407
+                [
1408
+                ],
1409
+                [
1410
+                    $file1UserShareInitiatorExpected,
1411
+                    $file1UserShareOtherExpected,
1412
+                    $file2UserShareOwnerExpected,
1413
+                ]
1414
+            ],
1415
+            // This might not happen in a real environment, as the combination
1416
+            // of shares does not seem to be possible on a folder without
1417
+            // resharing rights; if the folder has resharing rights then the
1418
+            // share with others would be included too in the results.
1419
+            [
1420
+                [
1421
+                    'node' => $folder,
1422
+                    'subfiles' => 'true',
1423
+                ],
1424
+                [
1425
+                    'file1' => [
1426
+                        IShare::TYPE_USER => [$file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1427
+                    ],
1428
+                ],
1429
+                [
1430
+                ],
1431
+                [
1432
+                    $file1UserShareInitiatorExpected,
1433
+                ]
1434
+            ],
1435
+            [
1436
+                [
1437
+                    'node' => $folder,
1438
+                    'subfiles' => 'true',
1439
+                ],
1440
+                [
1441
+                    'file1' => [
1442
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1443
+                        IShare::TYPE_GROUP => [$file1GroupShareRecipient],
1444
+                    ],
1445
+                ],
1446
+                [
1447
+                ],
1448
+                [
1449
+                    $file1UserShareOwnerExpected,
1450
+                    $file1GroupShareRecipientExpected,
1451
+                ]
1452
+            ],
1453
+            [
1454
+                [
1455
+                    'node' => $folder,
1456
+                    'subfiles' => 'true',
1457
+                ],
1458
+                [
1459
+                    'file1' => [
1460
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1461
+                        IShare::TYPE_GROUP => [$file1GroupShareOwner],
1462
+                        IShare::TYPE_LINK => [$file1LinkShareOwner],
1463
+                        IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1464
+                        IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1465
+                        IShare::TYPE_ROOM => [$file1RoomShareOwner],
1466
+                        IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1467
+                        IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1468
+                    ],
1469
+                ],
1470
+                [
1471
+                ],
1472
+                [
1473
+                    $file1UserShareOwnerExpected,
1474
+                    $file1GroupShareOwnerExpected,
1475
+                    $file1LinkShareOwnerExpected,
1476
+                    $file1EmailShareOwnerExpected,
1477
+                    $file1CircleShareOwnerExpected,
1478
+                    $file1RoomShareOwnerExpected,
1479
+                ]
1480
+            ],
1481
+            [
1482
+                [
1483
+                    'node' => $folder,
1484
+                    'subfiles' => 'true',
1485
+                ],
1486
+                [
1487
+                    'file1' => [
1488
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1489
+                        IShare::TYPE_GROUP => [$file1GroupShareOwner],
1490
+                        IShare::TYPE_LINK => [$file1LinkShareOwner],
1491
+                        IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1492
+                        IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1493
+                        IShare::TYPE_ROOM => [$file1RoomShareOwner],
1494
+                        IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1495
+                        IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1496
+                    ],
1497
+                ],
1498
+                [
1499
+                    IShare::TYPE_REMOTE => true,
1500
+                    IShare::TYPE_REMOTE_GROUP => true,
1501
+                ],
1502
+                [
1503
+                    $file1UserShareOwnerExpected,
1504
+                    $file1GroupShareOwnerExpected,
1505
+                    $file1LinkShareOwnerExpected,
1506
+                    $file1EmailShareOwnerExpected,
1507
+                    $file1CircleShareOwnerExpected,
1508
+                    $file1RoomShareOwnerExpected,
1509
+                    $file1RemoteShareOwnerExpected,
1510
+                    $file1RemoteGroupShareOwnerExpected,
1511
+                ]
1512
+            ],
1513
+        ];
1514
+
1515
+        return $data;
1516
+    }
1517
+
1518
+    private function mockSimpleNode(string $class, array $methods): MockObject {
1519
+        $node = $this->createMock($class);
1520
+        foreach ($methods as $method => $return) {
1521
+            if ($method === 'getDirectoryListing') {
1522
+                $return = array_map(
1523
+                    fn ($nodeParams) => $this->mockSimpleNode(...$nodeParams),
1524
+                    $return
1525
+                );
1526
+            }
1527
+            $node->method($method)->willReturn($return);
1528
+        }
1529
+        return $node;
1530
+    }
1531
+
1532
+    #[DataProvider('dataGetShares')]
1533
+    public function testGetShares(array $getSharesParameters, array $shares, array $extraShareTypes, array $expected): void {
1534
+        $shares = array_map(
1535
+            fn ($sharesByType) => array_map(
1536
+                fn ($shareList) => array_map(
1537
+                    function (array $shareParams): IShare {
1538
+                        $share = Server::get(IManager::class)->newShare();
1539
+                        $share->setShareType($shareParams['type'])
1540
+                            ->setSharedBy($shareParams['sharedBy'])
1541
+                            ->setShareOwner($shareParams['owner'])
1542
+                            ->setPermissions(Constants::PERMISSION_READ)
1543
+                            ->setId($shareParams['id']);
1544
+                        if (isset($shareParams['sharedWith'])) {
1545
+                            $share->setSharedWith($shareParams['sharedWith']);
1546
+                        }
1547
+                        if (isset($shareParams['sharedWithDisplayName'])) {
1548
+                            $share->setSharedWithDisplayName($shareParams['sharedWithDisplayName']);
1549
+                        }
1550
+                        if (isset($shareParams['sharedWithAvatar'])) {
1551
+                            $share->setSharedWithAvatar($shareParams['sharedWithAvatar']);
1552
+                        }
1553
+                        if (isset($shareParams['attributes'])) {
1554
+                            $shareAttributes = $this->createMock(IShareAttributes::class);
1555
+                            $shareAttributes->method('toArray')->willReturn($shareParams['attributes']);
1556
+                            $shareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
1557
+                            $share->setAttributes($shareAttributes);
1558
+
1559
+                            $expects['attributes'] = \json_encode($shareParams['attributes']);
1560
+                        }
1561
+                        if (isset($shareParams['node'])) {
1562
+                            $node = $this->mockSimpleNode(...$shareParams['node']);
1563
+                            $share->setNode($node);
1564
+                        }
1565
+                        if (isset($shareParams['note'])) {
1566
+                            $share->setNote($shareParams['note']);
1567
+                        }
1568
+                        if (isset($shareParams['expirationDate'])) {
1569
+                            $share->setExpirationDate($shareParams['expirationDate']);
1570
+                        }
1571
+                        if (isset($shareParams['token'])) {
1572
+                            $share->setToken($shareParams['token']);
1573
+                        }
1574
+                        if (isset($shareParams['label'])) {
1575
+                            $share->setLabel($shareParams['label']);
1576
+                        }
1577
+                        if (isset($shareParams['password'])) {
1578
+                            $share->setPassword($shareParams['password']);
1579
+                        }
1580
+                        if (isset($shareParams['sendPasswordByTalk'])) {
1581
+                            $share->setSendPasswordByTalk($shareParams['sendPasswordByTalk']);
1582
+                        }
1583
+                        return $share;
1584
+                    },
1585
+                    $shareList
1586
+                ),
1587
+                $sharesByType
1588
+            ),
1589
+            $shares
1590
+        );
1591
+
1592
+        /** @var ShareAPIController&MockObject $ocs */
1593
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
1594
+            ->setConstructorArgs([
1595
+                $this->appName,
1596
+                $this->request,
1597
+                $this->shareManager,
1598
+                $this->groupManager,
1599
+                $this->userManager,
1600
+                $this->rootFolder,
1601
+                $this->urlGenerator,
1602
+                $this->l,
1603
+                $this->config,
1604
+                $this->appConfig,
1605
+                $this->appManager,
1606
+                $this->serverContainer,
1607
+                $this->userStatusManager,
1608
+                $this->previewManager,
1609
+                $this->dateTimeZone,
1610
+                $this->logger,
1611
+                $this->factory,
1612
+                $this->mailer,
1613
+                $this->tagManager,
1614
+                $this->getEmailValidatorWithStrictEmailCheck(),
1615
+                $this->trustedServers,
1616
+                $this->currentUser,
1617
+            ])
1618
+            ->onlyMethods(['formatShare'])
1619
+            ->getMock();
1620
+
1621
+        $ocs->method('formatShare')
1622
+            ->willReturnCallback(
1623
+                function ($share) {
1624
+                    return [
1625
+                        'id' => $share->getId(),
1626
+                        'share_type' => $share->getShareType()
1627
+                    ];
1628
+                }
1629
+            );
1630
+
1631
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
1632
+        $userFolder->method('get')
1633
+            ->with('path')
1634
+            ->willReturn($this->mockSimpleNode(...$getSharesParameters['node']));
1635
+
1636
+        $this->rootFolder->method('getUserFolder')
1637
+            ->with($this->currentUser)
1638
+            ->willReturn($userFolder);
1639
+
1640
+        $this->shareManager
1641
+            ->method('getSharesBy')
1642
+            ->willReturnCallback(
1643
+                function ($user, $shareType, $node) use ($shares) {
1644
+                    if (!isset($shares[$node->getName()]) || !isset($shares[$node->getName()][$shareType])) {
1645
+                        return [];
1646
+                    }
1647
+                    return $shares[$node->getName()][$shareType];
1648
+                }
1649
+            );
1650
+
1651
+        $this->shareManager
1652
+            ->method('outgoingServer2ServerSharesAllowed')
1653
+            ->willReturn($extraShareTypes[ISHARE::TYPE_REMOTE] ?? false);
1654
+
1655
+        $this->shareManager
1656
+            ->method('outgoingServer2ServerGroupSharesAllowed')
1657
+            ->willReturn($extraShareTypes[ISHARE::TYPE_REMOTE_GROUP] ?? false);
1658
+
1659
+        $this->groupManager
1660
+            ->method('isInGroup')
1661
+            ->willReturnCallback(
1662
+                function ($user, $group) {
1663
+                    return $group === 'currentUserGroup';
1664
+                }
1665
+            );
1666
+
1667
+        $result = $ocs->getShares(
1668
+            $getSharesParameters['sharedWithMe'] ?? 'false',
1669
+            $getSharesParameters['reshares'] ?? 'false',
1670
+            $getSharesParameters['subfiles'] ?? 'false',
1671
+            'path'
1672
+        );
1673
+
1674
+        $this->assertEquals($expected, $result->getData());
1675
+    }
1676
+
1677
+    public function testCanAccessShareAsOwner(): void {
1678
+        $share = $this->createMock(IShare::class);
1679
+        $share->method('getShareOwner')->willReturn($this->currentUser);
1680
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1681
+    }
1682
+
1683
+    public function testCanAccessShareAsSharer(): void {
1684
+        $share = $this->createMock(IShare::class);
1685
+        $share->method('getSharedBy')->willReturn($this->currentUser);
1686
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1687
+    }
1688
+
1689
+    public function testCanAccessShareAsSharee(): void {
1690
+        $share = $this->createMock(IShare::class);
1691
+        $share->method('getShareType')->willReturn(IShare::TYPE_USER);
1692
+        $share->method('getSharedWith')->willReturn($this->currentUser);
1693
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1694
+    }
1695
+
1696
+    public function testCannotAccessLinkShare(): void {
1697
+        $share = $this->createMock(IShare::class);
1698
+        $share->method('getShareType')->willReturn(IShare::TYPE_LINK);
1699
+        $share->method('getNodeId')->willReturn(42);
1700
+
1701
+        $userFolder = $this->createMock(Folder::class);
1702
+        $this->rootFolder->method('getUserFolder')
1703
+            ->with($this->currentUser)
1704
+            ->willReturn($userFolder);
1705
+
1706
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1707
+    }
1708
+
1709
+    #[DataProvider('dataCanAccessShareWithPermissions')]
1710
+    public function testCanAccessShareWithPermissions(int $permissions, bool $expected): void {
1711
+        $share = $this->createMock(IShare::class);
1712
+        $share->method('getShareType')->willReturn(IShare::TYPE_USER);
1713
+        $share->method('getSharedWith')->willReturn($this->createMock(IUser::class));
1714
+        $share->method('getNodeId')->willReturn(42);
1715
+
1716
+        $file = $this->createMock(File::class);
1717
+
1718
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
1719
+        $userFolder->method('getFirstNodeById')
1720
+            ->with($share->getNodeId())
1721
+            ->willReturn($file);
1722
+        $userFolder->method('getById')
1723
+            ->with($share->getNodeId())
1724
+            ->willReturn([$file]);
1725
+        $this->rootFolder->method('getUserFolder')
1726
+            ->with($this->currentUser)
1727
+            ->willReturn($userFolder);
1728
+
1729
+        $file->method('getPermissions')
1730
+            ->willReturn($permissions);
1731
+
1732
+        if ($expected) {
1733
+            $this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1734
+        } else {
1735
+            $this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1736
+        }
1737
+    }
1738
+
1739
+    public static function dataCanAccessShareWithPermissions(): array {
1740
+        return [
1741
+            [Constants::PERMISSION_SHARE, true],
1742
+            [Constants::PERMISSION_READ, false],
1743
+            [Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, true],
1744
+        ];
1745
+    }
1746
+
1747
+    #[DataProvider('dataCanAccessShareAsGroupMember')]
1748
+    public function testCanAccessShareAsGroupMember(string $group, bool $expected): void {
1749
+        $share = $this->createMock(IShare::class);
1750
+        $share->method('getShareType')->willReturn(IShare::TYPE_GROUP);
1751
+        $share->method('getSharedWith')->willReturn($group);
1752
+        $share->method('getNodeId')->willReturn(42);
1753
+
1754
+        $file = $this->createMock(File::class);
1755
+
1756
+        $userFolder = $this->createMock(Folder::class);
1757
+        $userFolder->method('getFirstNodeById')
1758
+            ->with($share->getNodeId())
1759
+            ->willReturn($file);
1760
+        $userFolder->method('getById')
1761
+            ->with($share->getNodeId())
1762
+            ->willReturn([$file]);
1763
+        $this->rootFolder->method('getUserFolder')
1764
+            ->with($this->currentUser)
1765
+            ->willReturn($userFolder);
1766
+
1767
+        $user = $this->createMock(IUser::class);
1768
+        $this->userManager->method('get')
1769
+            ->with($this->currentUser)
1770
+            ->willReturn($user);
1771
+
1772
+        $group = $this->createMock(IGroup::class);
1773
+        $group->method('inGroup')->with($user)->willReturn(true);
1774
+        $group2 = $this->createMock(IGroup::class);
1775
+        $group2->method('inGroup')->with($user)->willReturn(false);
1776
+
1777
+        $this->groupManager->method('get')->willReturnMap([
1778
+            ['group', $group],
1779
+            ['group2', $group2],
1780
+            ['group-null', null],
1781
+        ]);
1782
+
1783
+        if ($expected) {
1784
+            $this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1785
+        } else {
1786
+            $this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1787
+        }
1788
+    }
1789
+
1790
+    public static function dataCanAccessShareAsGroupMember(): array {
1791
+        return [
1792
+            ['group', true],
1793
+            ['group2', false],
1794
+            ['group-null', false],
1795
+        ];
1796
+    }
1797
+
1798
+    public static function dataCanAccessRoomShare(): array {
1799
+        return [
1800
+            [false, false, false],
1801
+            [false, false, true],
1802
+            [true, true, true],
1803
+            [false, true, false],
1804
+        ];
1805
+    }
1806
+
1807
+    #[DataProvider('dataCanAccessRoomShare')]
1808
+    public function testCanAccessRoomShare(
1809
+        bool $expected,
1810
+        bool $helperAvailable,
1811
+        bool $canAccessShareByHelper,
1812
+    ): void {
1813
+        $share = $this->createMock(IShare::class);
1814
+        $share->method('getShareType')->willReturn(IShare::TYPE_ROOM);
1815
+        $share->method('getSharedWith')->willReturn('recipientRoom');
1816
+
1817
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
1818
+        $this->rootFolder->method('getUserFolder')
1819
+            ->with($this->currentUser)
1820
+            ->willReturn($userFolder);
1821
+
1822
+        $userFolder->method('getById')
1823
+            ->with($share->getNodeId())
1824
+            ->willReturn([$share->getNode()]);
1825
+
1826
+        if (!$helperAvailable) {
1827
+            $this->appManager->method('isEnabledForUser')
1828
+                ->with('spreed')
1829
+                ->willReturn(false);
1830
+        } else {
1831
+            $this->appManager->method('isEnabledForUser')
1832
+                ->with('spreed')
1833
+                ->willReturn(true);
1834
+
1835
+            // This is not possible anymore with PHPUnit 10+
1836
+            // as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
1837
+            // $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
1838
+            $helper = $this->getMockBuilder(\stdClass::class)
1839
+                ->addMethods(['canAccessShare'])
1840
+                ->getMock();
1841
+            $helper->method('canAccessShare')
1842
+                ->with($share, $this->currentUser)
1843
+                ->willReturn($canAccessShareByHelper);
1844
+
1845
+            $this->serverContainer->method('get')
1846
+                ->with('\OCA\Talk\Share\Helper\ShareAPIController')
1847
+                ->willReturn($helper);
1848
+        }
1849
+
1850
+        $this->assertEquals($expected, $this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1851
+    }
1852
+
1853
+
1854
+    public function testCreateShareNoPath(): void {
1855
+        $this->expectException(OCSNotFoundException::class);
1856
+        $this->expectExceptionMessage('Please specify a file or folder path');
1857
+
1858
+        $this->ocs->createShare();
1859
+    }
1860
+
1861
+
1862
+    public function testCreateShareInvalidPath(): void {
1863
+        $this->expectException(OCSNotFoundException::class);
1864
+        $this->expectExceptionMessage('Wrong path, file/folder does not exist');
1865
+
1866
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
1867
+        $this->rootFolder->expects($this->once())
1868
+            ->method('getUserFolder')
1869
+            ->with('currentUser')
1870
+            ->willReturn($userFolder);
1871
+
1872
+        $userFolder->expects($this->once())
1873
+            ->method('get')
1874
+            ->with('invalid-path')
1875
+            ->willThrowException(new NotFoundException());
1876
+
1877
+        $this->ocs->createShare('invalid-path');
1878
+    }
1879
+
1880
+    public function testCreateShareInvalidShareType(): void {
1881
+        $this->expectException(OCSBadRequestException::class);
1882
+        $this->expectExceptionMessage('Unknown share type');
1883
+
1884
+        $share = $this->newShare();
1885
+        $this->shareManager->method('newShare')->willReturn($share);
1886
+
1887
+        [$userFolder, $file] = $this->getNonSharedUserFile();
1888
+        $this->rootFolder->expects($this->atLeastOnce())
1889
+            ->method('getUserFolder')
1890
+            ->with('currentUser')
1891
+            ->willReturn($userFolder);
1892
+
1893
+        $userFolder->expects($this->atLeastOnce())
1894
+            ->method('get')
1895
+            ->with('valid-path')
1896
+            ->willReturn($file);
1897
+        $userFolder->method('getById')
1898
+            ->willReturn([]);
1899
+
1900
+        $file->expects($this->once())
1901
+            ->method('lock')
1902
+            ->with(ILockingProvider::LOCK_SHARED);
1903
+
1904
+        $this->ocs->createShare('valid-path', 31);
1905
+    }
1906
+
1907
+    public function testCreateShareUserNoShareWith(): void {
1908
+        $this->expectException(OCSNotFoundException::class);
1909
+        $this->expectExceptionMessage('Please specify a valid account to share with');
1910
+
1911
+        $share = $this->newShare();
1912
+        $this->shareManager->method('newShare')->willReturn($share);
1913
+
1914
+        [$userFolder, $path] = $this->getNonSharedUserFile();
1915
+        $this->rootFolder->method('getUserFolder')
1916
+            ->with('currentUser')
1917
+            ->willReturn($userFolder);
1918
+
1919
+        $userFolder->expects($this->once())
1920
+            ->method('get')
1921
+            ->with('valid-path')
1922
+            ->willReturn($path);
1923
+        $userFolder->method('getById')
1924
+            ->willReturn([]);
1925
+
1926
+        $path->expects($this->once())
1927
+            ->method('lock')
1928
+            ->with(ILockingProvider::LOCK_SHARED);
1929
+
1930
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER);
1931
+    }
1932
+
1933
+
1934
+    public function testCreateShareUserNoValidShareWith(): void {
1935
+        $this->expectException(OCSNotFoundException::class);
1936
+        $this->expectExceptionMessage('Please specify a valid account to share with');
1937
+
1938
+        $share = $this->newShare();
1939
+        $this->shareManager->method('newShare')->willReturn($share);
1940
+
1941
+        [$userFolder, $path] = $this->getNonSharedUserFile();
1942
+        $this->rootFolder->method('getUserFolder')
1943
+            ->with('currentUser')
1944
+            ->willReturn($userFolder);
1945
+
1946
+        $userFolder->expects($this->once())
1947
+            ->method('get')
1948
+            ->with('valid-path')
1949
+            ->willReturn($path);
1950
+        $userFolder->method('getById')
1951
+            ->willReturn([]);
1952
+        $path->expects($this->once())
1953
+            ->method('lock')
1954
+            ->with(ILockingProvider::LOCK_SHARED);
1955
+        $this->userManager->method('userExists')
1956
+            ->with('invalidUser')
1957
+            ->willReturn(false);
1958
+
1959
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER, 'invalidUser');
1960
+    }
1961
+
1962
+    public function testCreateShareUser(): void {
1963
+        $share = $this->newShare();
1964
+        $this->shareManager->method('newShare')->willReturn($share);
1965
+
1966
+        /** @var ShareAPIController $ocs */
1967
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
1968
+            ->setConstructorArgs([
1969
+                $this->appName,
1970
+                $this->request,
1971
+                $this->shareManager,
1972
+                $this->groupManager,
1973
+                $this->userManager,
1974
+                $this->rootFolder,
1975
+                $this->urlGenerator,
1976
+                $this->l,
1977
+                $this->config,
1978
+                $this->appConfig,
1979
+                $this->appManager,
1980
+                $this->serverContainer,
1981
+                $this->userStatusManager,
1982
+                $this->previewManager,
1983
+                $this->dateTimeZone,
1984
+                $this->logger,
1985
+                $this->factory,
1986
+                $this->mailer,
1987
+                $this->tagManager,
1988
+                $this->getEmailValidatorWithStrictEmailCheck(),
1989
+                $this->trustedServers,
1990
+                $this->currentUser,
1991
+            ])->onlyMethods(['formatShare'])
1992
+            ->getMock();
1993
+
1994
+        [$userFolder, $path] = $this->getNonSharedUserFile();
1995
+        $this->rootFolder->expects($this->exactly(2))
1996
+            ->method('getUserFolder')
1997
+            ->with('currentUser')
1998
+            ->willReturn($userFolder);
1999
+
2000
+        $userFolder->expects($this->once())
2001
+            ->method('get')
2002
+            ->with('valid-path')
2003
+            ->willReturn($path);
2004
+        $userFolder->method('getById')
2005
+            ->willReturn([]);
2006
+
2007
+        $this->userManager->method('userExists')->with('validUser')->willReturn(true);
2008
+
2009
+        $path->expects($this->once())
2010
+            ->method('lock')
2011
+            ->with(ILockingProvider::LOCK_SHARED);
2012
+
2013
+        $this->shareManager->method('createShare')
2014
+            ->with($this->callback(function (IShare $share) use ($path) {
2015
+                return $share->getNode() === $path
2016
+                    && $share->getPermissions() === (
2017
+                        Constants::PERMISSION_ALL
2018
+                        & ~Constants::PERMISSION_DELETE
2019
+                        & ~Constants::PERMISSION_CREATE
2020
+                    )
2021
+                    && $share->getShareType() === IShare::TYPE_USER
2022
+                    && $share->getSharedWith() === 'validUser'
2023
+                    && $share->getSharedBy() === 'currentUser';
2024
+            }))
2025
+            ->willReturnArgument(0);
2026
+
2027
+        $expected = new DataResponse([]);
2028
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER, 'validUser');
2029
+
2030
+        $this->assertInstanceOf(get_class($expected), $result);
2031
+        $this->assertEquals($expected->getData(), $result->getData());
2032
+    }
2033
+
2034
+
2035
+    public function testCreateShareGroupNoValidShareWith(): void {
2036
+        $this->expectException(OCSNotFoundException::class);
2037
+        $this->expectExceptionMessage('Please specify a valid group');
2038
+
2039
+        $share = $this->newShare();
2040
+        $this->shareManager->method('newShare')->willReturn($share);
2041
+        $this->shareManager->method('createShare')->willReturnArgument(0);
2042
+        $this->shareManager->method('allowGroupSharing')->willReturn(true);
2043
+
2044
+        [$userFolder, $path] = $this->getNonSharedUserFile();
2045
+        $this->rootFolder->method('getUserFolder')
2046
+            ->with('currentUser')
2047
+            ->willReturn($userFolder);
2048
+
2049
+        $userFolder->expects($this->once())
2050
+            ->method('get')
2051
+            ->with('valid-path')
2052
+            ->willReturn($path);
2053
+        $userFolder->method('getById')
2054
+            ->willReturn([]);
2055
+
2056
+        $path->expects($this->once())
2057
+            ->method('lock')
2058
+            ->with(ILockingProvider::LOCK_SHARED);
2059
+
2060
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_GROUP, 'invalidGroup');
2061
+    }
2062
+
2063
+    public function testCreateShareGroup(): void {
2064
+        $share = $this->newShare();
2065
+        $this->shareManager->method('newShare')->willReturn($share);
2066
+
2067
+        /** @var ShareAPIController&MockObject $ocs */
2068
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
2069
+            ->setConstructorArgs([
2070
+                $this->appName,
2071
+                $this->request,
2072
+                $this->shareManager,
2073
+                $this->groupManager,
2074
+                $this->userManager,
2075
+                $this->rootFolder,
2076
+                $this->urlGenerator,
2077
+                $this->l,
2078
+                $this->config,
2079
+                $this->appConfig,
2080
+                $this->appManager,
2081
+                $this->serverContainer,
2082
+                $this->userStatusManager,
2083
+                $this->previewManager,
2084
+                $this->dateTimeZone,
2085
+                $this->logger,
2086
+                $this->factory,
2087
+                $this->mailer,
2088
+                $this->tagManager,
2089
+                $this->getEmailValidatorWithStrictEmailCheck(),
2090
+                $this->trustedServers,
2091
+                $this->currentUser,
2092
+            ])->onlyMethods(['formatShare'])
2093
+            ->getMock();
2094
+
2095
+        $this->request
2096
+            ->method('getParam')
2097
+            ->willReturnMap([
2098
+                ['path', null, 'valid-path'],
2099
+                ['permissions', null, Constants::PERMISSION_ALL],
2100
+                ['shareType', '-1', IShare::TYPE_GROUP],
2101
+                ['shareWith', null, 'validGroup'],
2102
+            ]);
2103
+
2104
+        [$userFolder, $path] = $this->getNonSharedUserFolder();
2105
+        $this->rootFolder->expects($this->exactly(2))
2106
+            ->method('getUserFolder')
2107
+            ->with('currentUser')
2108
+            ->willReturn($userFolder);
2109
+
2110
+        $userFolder->expects($this->once())
2111
+            ->method('get')
2112
+            ->with('valid-path')
2113
+            ->willReturn($path);
2114
+        $userFolder->method('getById')
2115
+            ->willReturn([]);
2116
+
2117
+        $this->groupManager->method('groupExists')->with('validGroup')->willReturn(true);
2118
+
2119
+        $this->shareManager->expects($this->once())
2120
+            ->method('allowGroupSharing')
2121
+            ->willReturn(true);
2122
+
2123
+        $path->expects($this->once())
2124
+            ->method('lock')
2125
+            ->with(ILockingProvider::LOCK_SHARED);
2126
+
2127
+        $this->shareManager->method('createShare')
2128
+            ->with($this->callback(function (IShare $share) use ($path) {
2129
+                return $share->getNode() === $path
2130
+                && $share->getPermissions() === Constants::PERMISSION_ALL
2131
+                && $share->getShareType() === IShare::TYPE_GROUP
2132
+                && $share->getSharedWith() === 'validGroup'
2133
+                && $share->getSharedBy() === 'currentUser';
2134
+            }))
2135
+            ->willReturnArgument(0);
2136
+
2137
+        $expected = new DataResponse([]);
2138
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_GROUP, 'validGroup');
2139
+
2140
+        $this->assertInstanceOf(get_class($expected), $result);
2141
+        $this->assertEquals($expected->getData(), $result->getData());
2142
+    }
2143
+
2144
+
2145
+    public function testCreateShareGroupNotAllowed(): void {
2146
+        $this->expectException(OCSNotFoundException::class);
2147
+        $this->expectExceptionMessage('Group sharing is disabled by the administrator');
2148
+
2149
+        $share = $this->newShare();
2150
+        $this->shareManager->method('newShare')->willReturn($share);
2151
+
2152
+        [$userFolder, $path] = $this->getNonSharedUserFolder();
2153
+        $this->rootFolder->method('getUserFolder')
2154
+            ->with('currentUser')
2155
+            ->willReturn($userFolder);
2156
+
2157
+        $userFolder->expects($this->once())
2158
+            ->method('get')
2159
+            ->with('valid-path')
2160
+            ->willReturn($path);
2161
+        $userFolder->method('getById')
2162
+            ->willReturn([]);
2163
+
2164
+        $this->groupManager->method('groupExists')->with('validGroup')->willReturn(true);
2165
+
2166
+        $this->shareManager->expects($this->once())
2167
+            ->method('allowGroupSharing')
2168
+            ->willReturn(false);
2169
+
2170
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_GROUP, 'invalidGroup');
2171
+    }
2172
+
2173
+
2174
+    public function testCreateShareLinkNoLinksAllowed(): void {
2175
+        $this->expectException(OCSNotFoundException::class);
2176
+        $this->expectExceptionMessage('Public link sharing is disabled by the administrator');
2177
+
2178
+        $this->request
2179
+            ->method('getParam')
2180
+            ->willReturnMap([
2181
+                ['path', null, 'valid-path'],
2182
+                ['shareType', '-1', IShare::TYPE_LINK],
2183
+            ]);
2184
+
2185
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2186
+        $path->method('getId')->willReturn(42);
2187
+        $storage = $this->createMock(IStorage::class);
2188
+        $storage->method('instanceOfStorage')
2189
+            ->willReturnMap([
2190
+                ['OCA\Files_Sharing\External\Storage', false],
2191
+                ['OCA\Files_Sharing\SharedStorage', false],
2192
+            ]);
2193
+        $path->method('getStorage')->willReturn($storage);
2194
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2195
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2196
+        $this->rootFolder->method('getById')
2197
+            ->willReturn([]);
2198
+
2199
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2200
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2201
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(false);
2202
+
2203
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK);
2204
+    }
2205
+
2206
+
2207
+    public function testCreateShareLinkNoPublicUpload(): void {
2208
+        $this->expectException(OCSForbiddenException::class);
2209
+        $this->expectExceptionMessage('Public upload disabled by the administrator');
2210
+
2211
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2212
+        $path->method('getId')->willReturn(42);
2213
+        $storage = $this->createMock(IStorage::class);
2214
+        $storage->method('instanceOfStorage')
2215
+            ->willReturnMap([
2216
+                ['OCA\Files_Sharing\External\Storage', false],
2217
+                ['OCA\Files_Sharing\SharedStorage', false],
2218
+            ]);
2219
+        $path->method('getStorage')->willReturn($storage);
2220
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2221
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2222
+        $this->rootFolder->method('getById')
2223
+            ->willReturn([]);
2224
+
2225
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2226
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2227
+
2228
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true');
2229
+    }
2230
+
2231
+
2232
+    public function testCreateShareLinkPublicUploadFile(): void {
2233
+        $this->expectException(OCSBadRequestException::class);
2234
+        $this->expectExceptionMessage('Public upload is only possible for publicly shared folders');
2235
+
2236
+        $storage = $this->createMock(IStorage::class);
2237
+        $storage->method('instanceOfStorage')
2238
+            ->willReturnMap([
2239
+                ['OCA\Files_Sharing\External\Storage', false],
2240
+                ['OCA\Files_Sharing\SharedStorage', false],
2241
+            ]);
2242
+
2243
+        $file = $this->createMock(File::class);
2244
+        $file->method('getId')->willReturn(42);
2245
+        $file->method('getStorage')->willReturn($storage);
2246
+
2247
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2248
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($file);
2249
+        $this->rootFolder->method('getById')
2250
+            ->willReturn([]);
2251
+
2252
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2253
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2254
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2255
+
2256
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true');
2257
+    }
2258
+
2259
+    public function testCreateShareLinkPublicUploadFolder(): void {
2260
+        $ocs = $this->mockFormatShare();
2261
+
2262
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2263
+        $path->method('getId')->willReturn(1);
2264
+        $storage = $this->createMock(IStorage::class);
2265
+        $storage->method('instanceOfStorage')
2266
+            ->willReturnMap([
2267
+                ['OCA\Files_Sharing\External\Storage', false],
2268
+                ['OCA\Files_Sharing\SharedStorage', false],
2269
+            ]);
2270
+        $path->method('getStorage')->willReturn($storage);
2271
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2272
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2273
+        $this->rootFolder->method('getById')
2274
+            ->willReturn([]);
2275
+
2276
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2277
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2278
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2279
+
2280
+        $this->shareManager->expects($this->once())->method('createShare')->with(
2281
+            $this->callback(function (IShare $share) use ($path) {
2282
+                return $share->getNode() === $path
2283
+                    && $share->getShareType() === IShare::TYPE_LINK
2284
+                    && $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
2285
+                    && $share->getSharedBy() === 'currentUser'
2286
+                    && $share->getPassword() === null
2287
+                    && $share->getExpirationDate() === null;
2288
+            })
2289
+        )->willReturnArgument(0);
2290
+
2291
+        $expected = new DataResponse([]);
2292
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true', '', null, '');
2293
+
2294
+        $this->assertInstanceOf(get_class($expected), $result);
2295
+        $this->assertEquals($expected->getData(), $result->getData());
2296
+    }
2297
+
2298
+    public function testCreateShareLinkPassword(): void {
2299
+        $ocs = $this->mockFormatShare();
2300
+
2301
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2302
+        $path->method('getId')->willReturn(42);
2303
+        $storage = $this->createMock(IStorage::class);
2304
+        $storage->method('instanceOfStorage')
2305
+            ->willReturnMap([
2306
+                ['OCA\Files_Sharing\External\Storage', false],
2307
+                ['OCA\Files_Sharing\SharedStorage', false],
2308
+            ]);
2309
+        $path->method('getStorage')->willReturn($storage);
2310
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2311
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2312
+        $this->rootFolder->method('getById')
2313
+            ->willReturn([]);
2314
+
2315
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2316
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2317
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2318
+
2319
+        $this->shareManager->expects($this->once())->method('createShare')->with(
2320
+            $this->callback(function (IShare $share) use ($path) {
2321
+                return $share->getNode() === $path
2322
+                && $share->getShareType() === IShare::TYPE_LINK
2323
+                && $share->getPermissions() === Constants::PERMISSION_READ // publicUpload was set to false
2324
+                && $share->getSharedBy() === 'currentUser'
2325
+                && $share->getPassword() === 'password'
2326
+                && $share->getExpirationDate() === null;
2327
+            })
2328
+        )->willReturnArgument(0);
2329
+
2330
+        $expected = new DataResponse([]);
2331
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_READ, IShare::TYPE_LINK, null, 'false', 'password', null, '');
2332
+
2333
+        $this->assertInstanceOf(get_class($expected), $result);
2334
+        $this->assertEquals($expected->getData(), $result->getData());
2335
+    }
2336
+
2337
+    public function testCreateShareLinkSendPasswordByTalk(): void {
2338
+        $ocs = $this->mockFormatShare();
2339
+
2340
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2341
+        $path->method('getId')->willReturn(42);
2342
+        $storage = $this->createMock(IStorage::class);
2343
+        $storage->method('instanceOfStorage')
2344
+            ->willReturnMap([
2345
+                ['OCA\Files_Sharing\External\Storage', false],
2346
+                ['OCA\Files_Sharing\SharedStorage', false],
2347
+            ]);
2348
+        $path->method('getStorage')->willReturn($storage);
2349
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2350
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2351
+        $this->rootFolder->method('getById')
2352
+            ->willReturn([]);
2353
+
2354
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2355
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2356
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2357
+
2358
+        $this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(true);
2359
+
2360
+        $this->shareManager->expects($this->once())->method('createShare')->with(
2361
+            $this->callback(function (IShare $share) use ($path) {
2362
+                return $share->getNode() === $path
2363
+                && $share->getShareType() === IShare::TYPE_LINK
2364
+                && $share->getPermissions() === (Constants::PERMISSION_ALL & ~(Constants::PERMISSION_SHARE))
2365
+                && $share->getSharedBy() === 'currentUser'
2366
+                && $share->getPassword() === 'password'
2367
+                && $share->getSendPasswordByTalk() === true
2368
+                && $share->getExpirationDate() === null;
2369
+            })
2370
+        )->willReturnArgument(0);
2371
+
2372
+        $expected = new DataResponse([]);
2373
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true', 'password', 'true', '');
2374
+
2375
+        $this->assertInstanceOf(get_class($expected), $result);
2376
+        $this->assertEquals($expected->getData(), $result->getData());
2377
+    }
2378
+
2379
+
2380
+    public function testCreateShareLinkSendPasswordByTalkWithTalkDisabled(): void {
2381
+        $this->expectException(OCSForbiddenException::class);
2382
+        $this->expectExceptionMessage('Sharing valid-path sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled');
2383
+
2384
+        $ocs = $this->mockFormatShare();
2385
+
2386
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2387
+        $path->method('getId')->willReturn(42);
2388
+        $storage = $this->createMock(IStorage::class);
2389
+        $storage->method('instanceOfStorage')
2390
+            ->willReturnMap([
2391
+                ['OCA\Files_Sharing\External\Storage', false],
2392
+                ['OCA\Files_Sharing\SharedStorage', false],
2393
+            ]);
2394
+        $path->method('getStorage')->willReturn($storage);
2395
+        $path->method('getPath')->willReturn('valid-path');
2396
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2397
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2398
+        $this->rootFolder->method('getById')
2399
+            ->willReturn([]);
2400
+
2401
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2402
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2403
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2404
+
2405
+        $this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(false);
2406
+
2407
+        $this->shareManager->expects($this->never())->method('createShare');
2408
+
2409
+        $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', 'password', 'true', '');
2410
+    }
2411
+
2412
+    public function testCreateShareValidExpireDate(): void {
2413
+        $ocs = $this->mockFormatShare();
2414
+
2415
+        $this->request
2416
+            ->method('getParam')
2417
+            ->willReturnMap([
2418
+                ['path', null, 'valid-path'],
2419
+                ['shareType', '-1', IShare::TYPE_LINK],
2420
+                ['publicUpload', null, 'false'],
2421
+                ['expireDate', '', '2000-01-01'],
2422
+                ['password', '', ''],
2423
+            ]);
2424
+
2425
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2426
+        $path->method('getId')->willReturn(42);
2427
+        $storage = $this->createMock(IStorage::class);
2428
+        $storage->method('instanceOfStorage')
2429
+            ->willReturnMap([
2430
+                ['OCA\Files_Sharing\External\Storage', false],
2431
+                ['OCA\Files_Sharing\SharedStorage', false],
2432
+            ]);
2433
+        $path->method('getStorage')->willReturn($storage);
2434
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2435
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2436
+        $this->rootFolder->method('getById')
2437
+            ->willReturn([]);
2438
+
2439
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2440
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2441
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2442
+
2443
+        $this->shareManager->expects($this->once())->method('createShare')->with(
2444
+            $this->callback(function (IShare $share) use ($path) {
2445
+                $date = new \DateTime('2000-01-01');
2446
+                $date->setTime(0, 0, 0);
2447
+
2448
+                return $share->getNode() === $path
2449
+                && $share->getShareType() === IShare::TYPE_LINK
2450
+                && $share->getPermissions() === Constants::PERMISSION_READ | Constants::PERMISSION_SHARE
2451
+                && $share->getSharedBy() === 'currentUser'
2452
+                && $share->getPassword() === null
2453
+                && $share->getExpirationDate() == $date;
2454
+            })
2455
+        )->willReturnArgument(0);
2456
+
2457
+        $expected = new DataResponse([]);
2458
+        $result = $ocs->createShare('valid-path', null, IShare::TYPE_LINK, null, 'false', '', null, '2000-01-01');
2459
+
2460
+        $this->assertInstanceOf(get_class($expected), $result);
2461
+        $this->assertEquals($expected->getData(), $result->getData());
2462
+    }
2463
+
2464
+
2465
+    public function testCreateShareInvalidExpireDate(): void {
2466
+        $this->expectException(OCSNotFoundException::class);
2467
+        $this->expectExceptionMessage('Invalid date. Format must be YYYY-MM-DD');
2468
+
2469
+        $ocs = $this->mockFormatShare();
2470
+
2471
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2472
+        $path->method('getId')->willReturn(42);
2473
+        $storage = $this->createMock(IStorage::class);
2474
+        $storage->method('instanceOfStorage')
2475
+            ->willReturnMap([
2476
+                ['OCA\Files_Sharing\External\Storage', false],
2477
+                ['OCA\Files_Sharing\SharedStorage', false],
2478
+            ]);
2479
+        $path->method('getStorage')->willReturn($storage);
2480
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2481
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2482
+        $this->rootFolder->method('getById')
2483
+            ->willReturn([]);
2484
+
2485
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2486
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2487
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2488
+
2489
+        $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '', null, 'a1b2d3');
2490
+    }
2491
+
2492
+    public function testCreateShareRemote(): void {
2493
+        $share = $this->newShare();
2494
+        $this->shareManager->method('newShare')->willReturn($share);
2495
+
2496
+        /** @var ShareAPIController $ocs */
2497
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
2498
+            ->setConstructorArgs([
2499
+                $this->appName,
2500
+                $this->request,
2501
+                $this->shareManager,
2502
+                $this->groupManager,
2503
+                $this->userManager,
2504
+                $this->rootFolder,
2505
+                $this->urlGenerator,
2506
+                $this->l,
2507
+                $this->config,
2508
+                $this->appConfig,
2509
+                $this->appManager,
2510
+                $this->serverContainer,
2511
+                $this->userStatusManager,
2512
+                $this->previewManager,
2513
+                $this->dateTimeZone,
2514
+                $this->logger,
2515
+                $this->factory,
2516
+                $this->mailer,
2517
+                $this->tagManager,
2518
+                $this->getEmailValidatorWithStrictEmailCheck(),
2519
+                $this->trustedServers,
2520
+                $this->currentUser,
2521
+            ])->onlyMethods(['formatShare'])
2522
+            ->getMock();
2523
+
2524
+        [$userFolder, $path] = $this->getNonSharedUserFile();
2525
+        $this->rootFolder->expects($this->exactly(2))
2526
+            ->method('getUserFolder')
2527
+            ->with('currentUser')
2528
+            ->willReturn($userFolder);
2529
+
2530
+        $userFolder->expects($this->once())
2531
+            ->method('get')
2532
+            ->with('valid-path')
2533
+            ->willReturn($path);
2534
+        $userFolder->method('getById')
2535
+            ->willReturn([]);
2536
+
2537
+        $this->userManager->method('userExists')->with('validUser')->willReturn(true);
2538
+
2539
+        $path->expects($this->once())
2540
+            ->method('lock')
2541
+            ->with(ILockingProvider::LOCK_SHARED);
2542
+
2543
+        $this->shareManager->method('createShare')
2544
+            ->with($this->callback(function (IShare $share) use ($path) {
2545
+                return $share->getNode() === $path
2546
+                    && $share->getPermissions() === (
2547
+                        Constants::PERMISSION_ALL
2548
+                        & ~Constants::PERMISSION_DELETE
2549
+                        & ~Constants::PERMISSION_CREATE
2550
+                    )
2551
+                    && $share->getShareType() === IShare::TYPE_REMOTE
2552
+                    && $share->getSharedWith() === '[email protected]'
2553
+                    && $share->getSharedBy() === 'currentUser';
2554
+            }))
2555
+            ->willReturnArgument(0);
2556
+
2557
+        $this->shareManager->method('outgoingServer2ServerSharesAllowed')->willReturn(true);
2558
+
2559
+        $expected = new DataResponse([]);
2560
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_REMOTE, '[email protected]');
2561
+
2562
+        $this->assertInstanceOf(get_class($expected), $result);
2563
+        $this->assertEquals($expected->getData(), $result->getData());
2564
+    }
2565
+
2566
+    public function testCreateShareRemoteGroup(): void {
2567
+        $share = $this->newShare();
2568
+        $this->shareManager->method('newShare')->willReturn($share);
2569
+
2570
+        /** @var ShareAPIController $ocs */
2571
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
2572
+            ->setConstructorArgs([
2573
+                $this->appName,
2574
+                $this->request,
2575
+                $this->shareManager,
2576
+                $this->groupManager,
2577
+                $this->userManager,
2578
+                $this->rootFolder,
2579
+                $this->urlGenerator,
2580
+                $this->l,
2581
+                $this->config,
2582
+                $this->appConfig,
2583
+                $this->appManager,
2584
+                $this->serverContainer,
2585
+                $this->userStatusManager,
2586
+                $this->previewManager,
2587
+                $this->dateTimeZone,
2588
+                $this->logger,
2589
+                $this->factory,
2590
+                $this->mailer,
2591
+                $this->tagManager,
2592
+                $this->getEmailValidatorWithStrictEmailCheck(),
2593
+                $this->trustedServers,
2594
+                $this->currentUser,
2595
+            ])->onlyMethods(['formatShare'])
2596
+            ->getMock();
2597
+
2598
+        [$userFolder, $path] = $this->getNonSharedUserFile();
2599
+        $this->rootFolder->expects($this->exactly(2))
2600
+            ->method('getUserFolder')
2601
+            ->with('currentUser')
2602
+            ->willReturn($userFolder);
2603
+
2604
+        $userFolder->expects($this->once())
2605
+            ->method('get')
2606
+            ->with('valid-path')
2607
+            ->willReturn($path);
2608
+        $userFolder->method('getById')
2609
+            ->willReturn([]);
2610
+
2611
+        $this->userManager->method('userExists')->with('validUser')->willReturn(true);
2612
+
2613
+        $path->expects($this->once())
2614
+            ->method('lock')
2615
+            ->with(ILockingProvider::LOCK_SHARED);
2616
+
2617
+        $this->shareManager->method('createShare')
2618
+            ->with($this->callback(function (IShare $share) use ($path) {
2619
+                return $share->getNode() === $path
2620
+                    && $share->getPermissions() === (
2621
+                        Constants::PERMISSION_ALL
2622
+                        & ~Constants::PERMISSION_DELETE
2623
+                        & ~Constants::PERMISSION_CREATE
2624
+                    )
2625
+                    && $share->getShareType() === IShare::TYPE_REMOTE_GROUP
2626
+                    && $share->getSharedWith() === '[email protected]'
2627
+                    && $share->getSharedBy() === 'currentUser';
2628
+            }))
2629
+            ->willReturnArgument(0);
2630
+
2631
+        $this->shareManager->method('outgoingServer2ServerGroupSharesAllowed')->willReturn(true);
2632
+
2633
+        $expected = new DataResponse([]);
2634
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_REMOTE_GROUP, '[email protected]');
2635
+
2636
+        $this->assertInstanceOf(get_class($expected), $result);
2637
+        $this->assertEquals($expected->getData(), $result->getData());
2638
+    }
2639
+
2640
+    public function testCreateShareRoom(): void {
2641
+        $ocs = $this->mockFormatShare();
2642
+
2643
+        $share = $this->newShare();
2644
+        $this->shareManager->method('newShare')->willReturn($share);
2645
+
2646
+        [$userFolder, $path] = $this->getNonSharedUserFile();
2647
+        $this->rootFolder->expects($this->exactly(2))
2648
+            ->method('getUserFolder')
2649
+            ->with('currentUser')
2650
+            ->willReturn($userFolder);
2651
+
2652
+        $userFolder->expects($this->once())
2653
+            ->method('get')
2654
+            ->with('valid-path')
2655
+            ->willReturn($path);
2656
+        $userFolder->method('getById')
2657
+            ->willReturn([]);
2658
+
2659
+        $path->expects($this->once())
2660
+            ->method('lock')
2661
+            ->with(ILockingProvider::LOCK_SHARED);
2662
+
2663
+        $this->appManager->method('isEnabledForUser')
2664
+            ->with('spreed')
2665
+            ->willReturn(true);
2666
+
2667
+        // This is not possible anymore with PHPUnit 10+
2668
+        // as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
2669
+        // $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
2670
+        $helper = $this->getMockBuilder(\stdClass::class)
2671
+            ->addMethods(['createShare'])
2672
+            ->getMock();
2673
+        $helper->method('createShare')
2674
+            ->with(
2675
+                $share,
2676
+                'recipientRoom',
2677
+                Constants::PERMISSION_ALL
2678
+                & ~Constants::PERMISSION_DELETE
2679
+                & ~Constants::PERMISSION_CREATE,
2680
+                ''
2681
+            )->willReturnCallback(
2682
+                function ($share): void {
2683
+                    $share->setSharedWith('recipientRoom');
2684
+                    $share->setPermissions(Constants::PERMISSION_ALL);
2685
+                }
2686
+            );
2687
+
2688
+        $this->serverContainer->method('get')
2689
+            ->with('\OCA\Talk\Share\Helper\ShareAPIController')
2690
+            ->willReturn($helper);
2691
+
2692
+        $this->shareManager->method('createShare')
2693
+            ->with($this->callback(function (IShare $share) use ($path) {
2694
+                return $share->getNode() === $path
2695
+                    && $share->getPermissions() === Constants::PERMISSION_ALL
2696
+                    && $share->getShareType() === IShare::TYPE_ROOM
2697
+                    && $share->getSharedWith() === 'recipientRoom'
2698
+                    && $share->getSharedBy() === 'currentUser';
2699
+            }))
2700
+            ->willReturnArgument(0);
2701
+
2702
+        $expected = new DataResponse([]);
2703
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_ROOM, 'recipientRoom');
2704
+
2705
+        $this->assertInstanceOf(get_class($expected), $result);
2706
+        $this->assertEquals($expected->getData(), $result->getData());
2707
+    }
2708
+
2709
+
2710
+    public function testCreateShareRoomHelperNotAvailable(): void {
2711
+        $this->expectException(OCSForbiddenException::class);
2712
+        $this->expectExceptionMessage('Sharing valid-path failed because the back end does not support room shares');
2713
+
2714
+        $ocs = $this->mockFormatShare();
2715
+
2716
+        $share = $this->newShare();
2717
+        $this->shareManager->method('newShare')->willReturn($share);
2718
+
2719
+        [$userFolder, $path] = $this->getNonSharedUserFolder();
2720
+        $this->rootFolder->method('getUserFolder')
2721
+            ->with('currentUser')
2722
+            ->willReturn($userFolder);
2723
+
2724
+        $path->method('getPath')->willReturn('valid-path');
2725
+        $userFolder->expects($this->once())
2726
+            ->method('get')
2727
+            ->with('valid-path')
2728
+            ->willReturn($path);
2729
+        $userFolder->method('getById')
2730
+            ->willReturn([]);
2731
+
2732
+        $path->expects($this->once())
2733
+            ->method('lock')
2734
+            ->with(ILockingProvider::LOCK_SHARED);
2735
+
2736
+        $this->appManager->method('isEnabledForUser')
2737
+            ->with('spreed')
2738
+            ->willReturn(false);
2739
+
2740
+        $this->shareManager->expects($this->never())->method('createShare');
2741
+
2742
+        $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_ROOM, 'recipientRoom');
2743
+    }
2744
+
2745
+
2746
+    public function testCreateShareRoomHelperThrowException(): void {
2747
+        $this->expectException(OCSNotFoundException::class);
2748
+        $this->expectExceptionMessage('Exception thrown by the helper');
2749
+
2750
+        $ocs = $this->mockFormatShare();
2751
+
2752
+        $share = $this->newShare();
2753
+        $share->setSharedBy('currentUser');
2754
+        $this->shareManager->method('newShare')->willReturn($share);
2755
+
2756
+        [$userFolder, $path] = $this->getNonSharedUserFile();
2757
+        $this->rootFolder->method('getUserFolder')
2758
+            ->with('currentUser')
2759
+            ->willReturn($userFolder);
2760
+
2761
+        $userFolder->expects($this->once())
2762
+            ->method('get')
2763
+            ->with('valid-path')
2764
+            ->willReturn($path);
2765
+        $userFolder->method('getById')
2766
+            ->willReturn([]);
2767
+
2768
+        $path->expects($this->once())
2769
+            ->method('lock')
2770
+            ->with(ILockingProvider::LOCK_SHARED);
2771
+
2772
+        $this->appManager->method('isEnabledForUser')
2773
+            ->with('spreed')
2774
+            ->willReturn(true);
2775
+
2776
+        // This is not possible anymore with PHPUnit 10+
2777
+        // as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
2778
+        // $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
2779
+        $helper = $this->getMockBuilder(\stdClass::class)
2780
+            ->addMethods(['createShare'])
2781
+            ->getMock();
2782
+        $helper->method('createShare')
2783
+            ->with(
2784
+                $share,
2785
+                'recipientRoom',
2786
+                Constants::PERMISSION_ALL & ~(Constants::PERMISSION_CREATE | Constants::PERMISSION_DELETE),
2787
+                ''
2788
+            )->willReturnCallback(
2789
+                function ($share): void {
2790
+                    throw new OCSNotFoundException('Exception thrown by the helper');
2791
+                }
2792
+            );
2793
+
2794
+        $this->serverContainer->method('get')
2795
+            ->with('\OCA\Talk\Share\Helper\ShareAPIController')
2796
+            ->willReturn($helper);
2797
+
2798
+        $this->shareManager->expects($this->never())->method('createShare');
2799
+
2800
+        $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_ROOM, 'recipientRoom');
2801
+    }
2802
+
2803
+    /**
2804
+     * Test for https://github.com/owncloud/core/issues/22587
2805
+     * TODO: Remove once proper solution is in place
2806
+     */
2807
+    public function testCreateReshareOfFederatedMountNoDeletePermissions(): void {
2808
+        $share = Server::get(IManager::class)->newShare();
2809
+        $this->shareManager->method('newShare')->willReturn($share);
2810
+
2811
+        /** @var ShareAPIController&MockObject $ocs */
2812
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
2813
+            ->setConstructorArgs([
2814
+                $this->appName,
2815
+                $this->request,
2816
+                $this->shareManager,
2817
+                $this->groupManager,
2818
+                $this->userManager,
2819
+                $this->rootFolder,
2820
+                $this->urlGenerator,
2821
+                $this->l,
2822
+                $this->config,
2823
+                $this->appConfig,
2824
+                $this->appManager,
2825
+                $this->serverContainer,
2826
+                $this->userStatusManager,
2827
+                $this->previewManager,
2828
+                $this->dateTimeZone,
2829
+                $this->logger,
2830
+                $this->factory,
2831
+                $this->mailer,
2832
+                $this->tagManager,
2833
+                $this->getEmailValidatorWithStrictEmailCheck(),
2834
+                $this->trustedServers,
2835
+                $this->currentUser,
2836
+            ])->onlyMethods(['formatShare'])
2837
+            ->getMock();
2838
+
2839
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
2840
+        $this->rootFolder->expects($this->exactly(2))
2841
+            ->method('getUserFolder')
2842
+            ->with('currentUser')
2843
+            ->willReturn($userFolder);
2844
+
2845
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2846
+        $path->method('getId')->willReturn(42);
2847
+
2848
+        $storage = $this->createMock(IStorage::class);
2849
+        $storage->method('instanceOfStorage')
2850
+            ->willReturnMap([
2851
+                ['OCA\Files_Sharing\External\Storage', true],
2852
+                ['OCA\Files_Sharing\SharedStorage', false],
2853
+            ]);
2854
+        $userFolder->method('getStorage')->willReturn($storage);
2855
+        $path->method('getStorage')->willReturn($storage);
2856
+
2857
+        $path->method('getPermissions')->willReturn(Constants::PERMISSION_READ);
2858
+        $userFolder->expects($this->once())
2859
+            ->method('get')
2860
+            ->with('valid-path')
2861
+            ->willReturn($path);
2862
+        $userFolder->method('getById')
2863
+            ->willReturn([]);
2864
+
2865
+        $this->userManager->method('userExists')->with('validUser')->willReturn(true);
2866
+
2867
+        $this->shareManager
2868
+            ->expects($this->once())
2869
+            ->method('createShare')
2870
+            ->with($this->callback(function (IShare $share) {
2871
+                return $share->getPermissions() === Constants::PERMISSION_READ;
2872
+            }))
2873
+            ->willReturnArgument(0);
2874
+
2875
+        $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER, 'validUser');
2876
+    }
2877
+
2878
+
2879
+    public function testUpdateShareCantAccess(): void {
2880
+        $this->expectException(OCSNotFoundException::class);
2881
+        $this->expectExceptionMessage('Wrong share ID, share does not exist');
2882
+
2883
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
2884
+        $share = $this->newShare();
2885
+        $share->setNode($node);
2886
+
2887
+        $node->expects($this->once())
2888
+            ->method('lock')
2889
+            ->with(ILockingProvider::LOCK_SHARED);
2890
+
2891
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2892
+
2893
+        $this->rootFolder->method('getUserFolder')
2894
+            ->with($this->currentUser)
2895
+            ->willReturn($userFolder);
2896
+
2897
+        $userFolder->method('getById')
2898
+            ->with($share->getNodeId())
2899
+            ->willReturn([$share->getNode()]);
2900
+
2901
+        $this->ocs->updateShare(42);
2902
+    }
2903
+
2904
+
2905
+    public function testUpdateNoParametersLink(): void {
2906
+        $this->expectException(OCSBadRequestException::class);
2907
+        $this->expectExceptionMessage('Wrong or no update parameter given');
2908
+
2909
+        $node = $this->getMockBuilder(Folder::class)->getMock();
2910
+        $share = $this->newShare();
2911
+        $share->setPermissions(Constants::PERMISSION_ALL)
2912
+            ->setSharedBy($this->currentUser)
2913
+            ->setShareType(IShare::TYPE_LINK)
2914
+            ->setNode($node);
2915
+
2916
+        $node->expects($this->once())
2917
+            ->method('lock')
2918
+            ->with(ILockingProvider::LOCK_SHARED);
2919
+
2920
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2921
+
2922
+        $this->ocs->updateShare(42);
2923
+    }
2924
+
2925
+
2926
+    public function testUpdateNoParametersOther(): void {
2927
+        $this->expectException(OCSBadRequestException::class);
2928
+        $this->expectExceptionMessage('Wrong or no update parameter given');
2929
+
2930
+        $node = $this->getMockBuilder(Folder::class)->getMock();
2931
+        $share = $this->newShare();
2932
+        $share->setPermissions(Constants::PERMISSION_ALL)
2933
+            ->setSharedBy($this->currentUser)
2934
+            ->setShareType(IShare::TYPE_GROUP)
2935
+            ->setNode($node);
2936
+
2937
+        $node->expects($this->once())
2938
+            ->method('lock')
2939
+            ->with(ILockingProvider::LOCK_SHARED);
2940
+
2941
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2942
+
2943
+        $this->ocs->updateShare(42);
2944
+    }
2945
+
2946
+    public function testUpdateLinkShareClear(): void {
2947
+        $ocs = $this->mockFormatShare();
2948
+
2949
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
2950
+        $node->method('getId')
2951
+            ->willReturn(42);
2952
+        $share = $this->newShare();
2953
+        $share->setPermissions(Constants::PERMISSION_ALL)
2954
+            ->setSharedBy($this->currentUser)
2955
+            ->setShareType(IShare::TYPE_LINK)
2956
+            ->setPassword('password')
2957
+            ->setExpirationDate(new \DateTime())
2958
+            ->setNote('note')
2959
+            ->setLabel('label')
2960
+            ->setHideDownload(true)
2961
+            ->setPermissions(Constants::PERMISSION_ALL)
2962
+            ->setNode($node);
2963
+
2964
+        $node->expects($this->once())
2965
+            ->method('lock')
2966
+            ->with(ILockingProvider::LOCK_SHARED);
2967
+
2968
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2969
+
2970
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
2971
+            $this->callback(function (IShare $share) {
2972
+                return $share->getPermissions() === Constants::PERMISSION_READ
2973
+                && $share->getPassword() === null
2974
+                && $share->getExpirationDate() === null
2975
+                // Once set a note or a label are never back to null, only to an
2976
+                // empty string.
2977
+                && $share->getNote() === ''
2978
+                && $share->getLabel() === ''
2979
+                && $share->getHideDownload() === false;
2980
+            })
2981
+        )->willReturnArgument(0);
2982
+
2983
+        $this->shareManager->method('getSharedWith')
2984
+            ->willReturn([]);
2985
+
2986
+        $this->rootFolder->method('getUserFolder')
2987
+            ->with($this->currentUser)
2988
+            ->willReturn($userFolder);
2989
+
2990
+        $userFolder->method('getById')
2991
+            ->with(42)
2992
+            ->willReturn([$node]);
2993
+        $userFolder->method('getFirstNodeById')
2994
+            ->with(42)
2995
+            ->willReturn($node);
2996
+
2997
+        $mountPoint = $this->createMock(IMountPoint::class);
2998
+        $node->method('getMountPoint')
2999
+            ->willReturn($mountPoint);
3000
+        $mountPoint->method('getStorageRootId')
3001
+            ->willReturn(42);
3002
+
3003
+        $expected = new DataResponse([]);
3004
+        $result = $ocs->updateShare(42, null, '', null, 'false', '', '', '', 'false');
3005
+
3006
+        $this->assertInstanceOf(get_class($expected), $result);
3007
+        $this->assertEquals($expected->getData(), $result->getData());
3008
+    }
3009
+
3010
+    public function testUpdateLinkShareSet(): void {
3011
+        $ocs = $this->mockFormatShare();
3012
+
3013
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3014
+        $folder->method('getId')
3015
+            ->willReturn(42);
3016
+
3017
+        $share = Server::get(IManager::class)->newShare();
3018
+        $share->setPermissions(Constants::PERMISSION_ALL)
3019
+            ->setSharedBy($this->currentUser)
3020
+            ->setShareType(IShare::TYPE_LINK)
3021
+            ->setNode($folder);
3022
+
3023
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3024
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3025
+
3026
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3027
+            $this->callback(function (IShare $share) {
3028
+                $date = new \DateTime('2000-01-01');
3029
+                $date->setTime(0, 0, 0);
3030
+
3031
+                return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3032
+                && $share->getPassword() === 'password'
3033
+                && $share->getExpirationDate() == $date
3034
+                && $share->getNote() === 'note'
3035
+                && $share->getLabel() === 'label'
3036
+                && $share->getHideDownload() === true;
3037
+            })
3038
+        )->willReturnArgument(0);
3039
+
3040
+        $this->shareManager->method('getSharedWith')
3041
+            ->willReturn([]);
3042
+
3043
+        $this->rootFolder->method('getUserFolder')
3044
+            ->with($this->currentUser)
3045
+            ->willReturn($userFolder);
3046
+
3047
+        $userFolder->method('getById')
3048
+            ->with(42)
3049
+            ->willReturn([$folder]);
3050
+
3051
+        $mountPoint = $this->createMock(IMountPoint::class);
3052
+        $folder->method('getMountPoint')
3053
+            ->willReturn($mountPoint);
3054
+        $mountPoint->method('getStorageRootId')
3055
+            ->willReturn(42);
3056
+
3057
+        $expected = new DataResponse([]);
3058
+        $result = $ocs->updateShare(42, null, 'password', null, 'true', '2000-01-01', 'note', 'label', 'true');
3059
+
3060
+        $this->assertInstanceOf(get_class($expected), $result);
3061
+        $this->assertEquals($expected->getData(), $result->getData());
3062
+    }
3063
+
3064
+    #[DataProvider('publicUploadParamsProvider')]
3065
+    public function testUpdateLinkShareEnablePublicUpload($permissions, $publicUpload, $expireDate, $password): void {
3066
+        $ocs = $this->mockFormatShare();
3067
+
3068
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3069
+        $folder->method('getId')
3070
+            ->willReturn(42);
3071
+
3072
+        $share = Server::get(IManager::class)->newShare();
3073
+        $share->setPermissions(Constants::PERMISSION_ALL)
3074
+            ->setSharedBy($this->currentUser)
3075
+            ->setShareType(IShare::TYPE_LINK)
3076
+            ->setPassword('password')
3077
+            ->setNode($folder);
3078
+
3079
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3080
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3081
+        $this->shareManager->method('getSharedWith')->willReturn([]);
3082
+
3083
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3084
+            $this->callback(function (IShare $share) {
3085
+                return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3086
+                && $share->getPassword() === 'password'
3087
+                && $share->getExpirationDate() === null;
3088
+            })
3089
+        )->willReturnArgument(0);
3090
+
3091
+        $this->rootFolder->method('getUserFolder')
3092
+            ->with($this->currentUser)
3093
+            ->willReturn($userFolder);
3094
+
3095
+        $userFolder->method('getById')
3096
+            ->with(42)
3097
+            ->willReturn([$folder]);
3098
+
3099
+        $mountPoint = $this->createMock(IMountPoint::class);
3100
+        $folder->method('getMountPoint')
3101
+            ->willReturn($mountPoint);
3102
+        $mountPoint->method('getStorageRootId')
3103
+            ->willReturn(42);
3104
+
3105
+        $expected = new DataResponse([]);
3106
+        $result = $ocs->updateShare(42, $permissions, $password, null, $publicUpload, $expireDate);
3107
+
3108
+        $this->assertInstanceOf(get_class($expected), $result);
3109
+        $this->assertEquals($expected->getData(), $result->getData());
3110
+    }
3111
+
3112
+
3113
+    public static function publicLinkValidPermissionsProvider() {
3114
+        return [
3115
+            [Constants::PERMISSION_CREATE],
3116
+            [Constants::PERMISSION_READ],
3117
+            [Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE],
3118
+            [Constants::PERMISSION_READ | Constants::PERMISSION_DELETE],
3119
+            [Constants::PERMISSION_READ | Constants::PERMISSION_CREATE],
3120
+        ];
3121
+    }
3122
+
3123
+    #[DataProvider('publicLinkValidPermissionsProvider')]
3124
+    public function testUpdateLinkShareSetCRUDPermissions($permissions): void {
3125
+        $ocs = $this->mockFormatShare();
3126
+
3127
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3128
+        $folder->method('getId')
3129
+            ->willReturn(42);
3130
+
3131
+        $share = Server::get(IManager::class)->newShare();
3132
+        $share->setPermissions(Constants::PERMISSION_ALL)
3133
+            ->setSharedBy($this->currentUser)
3134
+            ->setShareType(IShare::TYPE_LINK)
3135
+            ->setPassword('password')
3136
+            ->setNode($folder);
3137
+
3138
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3139
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3140
+        $this->shareManager->method('getSharedWith')->willReturn([]);
3141
+
3142
+        $this->shareManager
3143
+            ->expects($this->any())
3144
+            ->method('updateShare')
3145
+            ->willReturnArgument(0);
3146
+
3147
+        $this->rootFolder->method('getUserFolder')
3148
+            ->with($this->currentUser)
3149
+            ->willReturn($userFolder);
3150
+
3151
+        $userFolder->method('getById')
3152
+            ->with(42)
3153
+            ->willReturn([$folder]);
3154
+
3155
+        $mountPoint = $this->createMock(IMountPoint::class);
3156
+        $folder->method('getMountPoint')
3157
+            ->willReturn($mountPoint);
3158
+        $mountPoint->method('getStorageRootId')
3159
+            ->willReturn(42);
3160
+
3161
+        $expected = new DataResponse([]);
3162
+        $result = $ocs->updateShare(42, $permissions, 'password', null, null, null);
3163
+
3164
+        $this->assertInstanceOf(get_class($expected), $result);
3165
+        $this->assertEquals($expected->getData(), $result->getData());
3166
+    }
3167
+
3168
+    public static function publicLinkInvalidPermissionsProvider1() {
3169
+        return [
3170
+            [Constants::PERMISSION_DELETE],
3171
+            [Constants::PERMISSION_UPDATE],
3172
+            [Constants::PERMISSION_SHARE],
3173
+        ];
3174
+    }
3175
+
3176
+    #[DataProvider('publicLinkInvalidPermissionsProvider1')]
3177
+    public function testUpdateLinkShareSetInvalidCRUDPermissions1($permissions): void {
3178
+        $this->expectException(OCSBadRequestException::class);
3179
+        $this->expectExceptionMessage('Share must at least have READ or CREATE permissions');
3180
+
3181
+        $this->testUpdateLinkShareSetCRUDPermissions($permissions, null);
3182
+    }
3183
+
3184
+    public static function publicLinkInvalidPermissionsProvider2() {
3185
+        return [
3186
+            [Constants::PERMISSION_CREATE | Constants::PERMISSION_DELETE],
3187
+            [Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE],
3188
+        ];
3189
+    }
3190
+
3191
+    #[DataProvider('publicLinkInvalidPermissionsProvider2')]
3192
+    public function testUpdateLinkShareSetInvalidCRUDPermissions2($permissions): void {
3193
+        $this->expectException(OCSBadRequestException::class);
3194
+        $this->expectExceptionMessage('Share must have READ permission if UPDATE or DELETE permission is set');
3195
+
3196
+        $this->testUpdateLinkShareSetCRUDPermissions($permissions);
3197
+    }
3198
+
3199
+    public function testUpdateLinkShareInvalidDate(): void {
3200
+        $this->expectException(OCSBadRequestException::class);
3201
+        $this->expectExceptionMessage('Invalid date. Format must be YYYY-MM-DD');
3202
+
3203
+        $ocs = $this->mockFormatShare();
3204
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3205
+        $userFolder->method('getById')
3206
+            ->with(42)
3207
+            ->willReturn([$folder]);
3208
+        $this->rootFolder->method('getUserFolder')
3209
+            ->with($this->currentUser)
3210
+            ->willReturn($userFolder);
3211
+
3212
+        $folder->method('getId')
3213
+            ->willReturn(42);
3214
+
3215
+        $share = Server::get(IManager::class)->newShare();
3216
+        $share->setPermissions(Constants::PERMISSION_ALL)
3217
+            ->setSharedBy($this->currentUser)
3218
+            ->setShareType(IShare::TYPE_LINK)
3219
+            ->setNode($folder);
3220
+
3221
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3222
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3223
+
3224
+        $ocs->updateShare(42, null, 'password', null, 'true', '2000-01-a');
3225
+    }
3226
+
3227
+    public static function publicUploadParamsProvider() {
3228
+        return [
3229
+            [null, 'true', null, 'password'],
3230
+            // legacy had no delete
3231
+            [
3232
+                Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE,
3233
+                'true', null, 'password'
3234
+            ],
3235
+            // correct
3236
+            [
3237
+                Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE,
3238
+                null, null, 'password'
3239
+            ],
3240
+        ];
3241
+    }
3242
+
3243
+    #[DataProvider('publicUploadParamsProvider')]
3244
+    public function testUpdateLinkSharePublicUploadNotAllowed($permissions, $publicUpload, $expireDate, $password): void {
3245
+        $this->expectException(OCSForbiddenException::class);
3246
+        $this->expectExceptionMessage('Public upload disabled by the administrator');
3247
+
3248
+        $ocs = $this->mockFormatShare();
3249
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3250
+        $userFolder->method('getById')
3251
+            ->with(42)
3252
+            ->willReturn([$folder]);
3253
+        $this->rootFolder->method('getUserFolder')
3254
+            ->with($this->currentUser)
3255
+            ->willReturn($userFolder);
3256
+
3257
+        $folder->method('getId')->willReturn(42);
3258
+
3259
+        $share = Server::get(IManager::class)->newShare();
3260
+        $share->setPermissions(Constants::PERMISSION_ALL)
3261
+            ->setSharedBy($this->currentUser)
3262
+            ->setShareType(IShare::TYPE_LINK)
3263
+            ->setNode($folder);
3264
+
3265
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3266
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(false);
3267
+
3268
+        $ocs->updateShare(42, $permissions, $password, null, $publicUpload, $expireDate);
3269
+    }
3270
+
3271
+
3272
+    public function testUpdateLinkSharePublicUploadOnFile(): void {
3273
+        $this->expectException(OCSBadRequestException::class);
3274
+        $this->expectExceptionMessage('Public upload is only possible for publicly shared folders');
3275
+
3276
+        $ocs = $this->mockFormatShare();
3277
+
3278
+        $file = $this->getMockBuilder(File::class)->getMock();
3279
+        $file->method('getId')
3280
+            ->willReturn(42);
3281
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3282
+        $userFolder->method('getById')
3283
+            ->with(42)
3284
+            ->willReturn([$folder]);
3285
+        $this->rootFolder->method('getUserFolder')
3286
+            ->with($this->currentUser)
3287
+            ->willReturn($userFolder);
3288
+
3289
+        $share = Server::get(IManager::class)->newShare();
3290
+        $share->setPermissions(Constants::PERMISSION_ALL)
3291
+            ->setSharedBy($this->currentUser)
3292
+            ->setShareType(IShare::TYPE_LINK)
3293
+            ->setNode($file);
3294
+
3295
+        $this->shareManager
3296
+            ->method('getShareById')
3297
+            ->with('ocinternal:42')
3298
+            ->willReturn($share);
3299
+        $this->shareManager
3300
+            ->method('shareApiLinkAllowPublicUpload')
3301
+            ->willReturn(true);
3302
+        $this->shareManager
3303
+            ->method('updateShare')
3304
+            ->with($share)
3305
+            ->willThrowException(new \InvalidArgumentException('File shares cannot have create or delete permissions'));
3306
+
3307
+        $ocs->updateShare(42, null, 'password', null, 'true', '');
3308
+    }
3309
+
3310
+    public function testUpdateLinkSharePasswordDoesNotChangeOther(): void {
3311
+        $ocs = $this->mockFormatShare();
3312
+
3313
+        $date = new \DateTime('2000-01-01');
3314
+        $date->setTime(0, 0, 0);
3315
+
3316
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
3317
+        $node->method('getId')->willReturn(42);
3318
+        $userFolder->method('getById')
3319
+            ->with(42)
3320
+            ->willReturn([$node]);
3321
+        $this->rootFolder->method('getUserFolder')
3322
+            ->with($this->currentUser)
3323
+            ->willReturn($userFolder);
3324
+        $share = $this->newShare();
3325
+        $share->setPermissions(Constants::PERMISSION_ALL)
3326
+            ->setSharedBy($this->currentUser)
3327
+            ->setShareType(IShare::TYPE_LINK)
3328
+            ->setPassword('password')
3329
+            ->setSendPasswordByTalk(true)
3330
+            ->setExpirationDate($date)
3331
+            ->setNote('note')
3332
+            ->setLabel('label')
3333
+            ->setHideDownload(true)
3334
+            ->setPermissions(Constants::PERMISSION_ALL)
3335
+            ->setNode($node);
3336
+
3337
+        $node->expects($this->once())
3338
+            ->method('lock')
3339
+            ->with(ILockingProvider::LOCK_SHARED);
3340
+
3341
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3342
+
3343
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3344
+            $this->callback(function (IShare $share) use ($date) {
3345
+                return $share->getPermissions() === Constants::PERMISSION_ALL
3346
+                && $share->getPassword() === 'newpassword'
3347
+                && $share->getSendPasswordByTalk() === true
3348
+                && $share->getExpirationDate() === $date
3349
+                && $share->getNote() === 'note'
3350
+                && $share->getLabel() === 'label'
3351
+                && $share->getHideDownload() === true;
3352
+            })
3353
+        )->willReturnArgument(0);
3354
+
3355
+        $expected = new DataResponse([]);
3356
+        $result = $ocs->updateShare(42, null, 'newpassword', null, null, null, null, null, null);
3357
+
3358
+        $this->assertInstanceOf(get_class($expected), $result);
3359
+        $this->assertEquals($expected->getData(), $result->getData());
3360
+    }
3361
+
3362
+    public function testUpdateLinkShareSendPasswordByTalkDoesNotChangeOther(): void {
3363
+        $ocs = $this->mockFormatShare();
3364
+
3365
+        $date = new \DateTime('2000-01-01');
3366
+        $date->setTime(0, 0, 0);
3367
+
3368
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
3369
+        $userFolder->method('getById')
3370
+            ->with(42)
3371
+            ->willReturn([$node]);
3372
+        $this->rootFolder->method('getUserFolder')
3373
+            ->with($this->currentUser)
3374
+            ->willReturn($userFolder);
3375
+        $node->method('getId')->willReturn(42);
3376
+        $share = $this->newShare();
3377
+        $share->setPermissions(Constants::PERMISSION_ALL)
3378
+            ->setSharedBy($this->currentUser)
3379
+            ->setShareType(IShare::TYPE_LINK)
3380
+            ->setPassword('password')
3381
+            ->setSendPasswordByTalk(false)
3382
+            ->setExpirationDate($date)
3383
+            ->setNote('note')
3384
+            ->setLabel('label')
3385
+            ->setHideDownload(true)
3386
+            ->setPermissions(Constants::PERMISSION_ALL)
3387
+            ->setNode($node);
3388
+
3389
+        $node->expects($this->once())
3390
+            ->method('lock')
3391
+            ->with(ILockingProvider::LOCK_SHARED);
3392
+
3393
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3394
+
3395
+        $this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(true);
3396
+
3397
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3398
+            $this->callback(function (IShare $share) use ($date) {
3399
+                return $share->getPermissions() === Constants::PERMISSION_ALL
3400
+                && $share->getPassword() === 'password'
3401
+                && $share->getSendPasswordByTalk() === true
3402
+                && $share->getExpirationDate() === $date
3403
+                && $share->getNote() === 'note'
3404
+                && $share->getLabel() === 'label'
3405
+                && $share->getHideDownload() === true;
3406
+            })
3407
+        )->willReturnArgument(0);
3408
+
3409
+        $expected = new DataResponse([]);
3410
+        $result = $ocs->updateShare(42, null, null, 'true', null, null, null, null, null);
3411
+
3412
+        $this->assertInstanceOf(get_class($expected), $result);
3413
+        $this->assertEquals($expected->getData(), $result->getData());
3414
+    }
3415
+
3416
+
3417
+    public function testUpdateLinkShareSendPasswordByTalkWithTalkDisabledDoesNotChangeOther(): void {
3418
+        $this->expectException(OCSForbiddenException::class);
3419
+        $this->expectExceptionMessage('"Sending the password by Nextcloud Talk" for sharing a file or folder failed because Nextcloud Talk is not enabled.');
3420
+
3421
+        $ocs = $this->mockFormatShare();
3422
+
3423
+        $date = new \DateTime('2000-01-01');
3424
+        $date->setTime(0, 0, 0);
3425
+
3426
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
3427
+        $userFolder->method('getById')
3428
+            ->with(42)
3429
+            ->willReturn([$node]);
3430
+        $this->rootFolder->method('getUserFolder')
3431
+            ->with($this->currentUser)
3432
+            ->willReturn($userFolder);
3433
+        $node->method('getId')->willReturn(42);
3434
+        $share = $this->newShare();
3435
+        $share->setPermissions(Constants::PERMISSION_ALL)
3436
+            ->setSharedBy($this->currentUser)
3437
+            ->setShareType(IShare::TYPE_LINK)
3438
+            ->setPassword('password')
3439
+            ->setSendPasswordByTalk(false)
3440
+            ->setExpirationDate($date)
3441
+            ->setNote('note')
3442
+            ->setLabel('label')
3443
+            ->setHideDownload(true)
3444
+            ->setPermissions(Constants::PERMISSION_ALL)
3445
+            ->setNode($node);
3446
+
3447
+        $node->expects($this->once())
3448
+            ->method('lock')
3449
+            ->with(ILockingProvider::LOCK_SHARED);
3450
+
3451
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3452
+
3453
+        $this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(false);
3454
+
3455
+        $this->shareManager->expects($this->never())->method('updateShare');
3456
+
3457
+        $ocs->updateShare(42, null, null, 'true', null, null, null, null, null);
3458
+    }
3459
+
3460
+    public function testUpdateLinkShareDoNotSendPasswordByTalkDoesNotChangeOther(): void {
3461
+        $ocs = $this->mockFormatShare();
3462
+
3463
+        $date = new \DateTime('2000-01-01');
3464
+        $date->setTime(0, 0, 0);
3465
+
3466
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
3467
+        $userFolder->method('getById')
3468
+            ->with(42)
3469
+            ->willReturn([$node]);
3470
+        $this->rootFolder->method('getUserFolder')
3471
+            ->with($this->currentUser)
3472
+            ->willReturn($userFolder);
3473
+        $node->method('getId')->willReturn(42);
3474
+        $share = $this->newShare();
3475
+        $share->setPermissions(Constants::PERMISSION_ALL)
3476
+            ->setSharedBy($this->currentUser)
3477
+            ->setShareType(IShare::TYPE_LINK)
3478
+            ->setPassword('password')
3479
+            ->setSendPasswordByTalk(true)
3480
+            ->setExpirationDate($date)
3481
+            ->setNote('note')
3482
+            ->setLabel('label')
3483
+            ->setHideDownload(true)
3484
+            ->setPermissions(Constants::PERMISSION_ALL)
3485
+            ->setNode($node);
3486
+
3487
+        $node->expects($this->once())
3488
+            ->method('lock')
3489
+            ->with(ILockingProvider::LOCK_SHARED);
3490
+
3491
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3492
+
3493
+        $this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(true);
3494
+
3495
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3496
+            $this->callback(function (IShare $share) use ($date) {
3497
+                return $share->getPermissions() === Constants::PERMISSION_ALL
3498
+                && $share->getPassword() === 'password'
3499
+                && $share->getSendPasswordByTalk() === false
3500
+                && $share->getExpirationDate() === $date
3501
+                && $share->getNote() === 'note'
3502
+                && $share->getLabel() === 'label'
3503
+                && $share->getHideDownload() === true;
3504
+            })
3505
+        )->willReturnArgument(0);
3506
+
3507
+        $expected = new DataResponse([]);
3508
+        $result = $ocs->updateShare(42, null, null, 'false', null, null, null, null, null);
3509
+
3510
+        $this->assertInstanceOf(get_class($expected), $result);
3511
+        $this->assertEquals($expected->getData(), $result->getData());
3512
+    }
3513
+
3514
+    public function testUpdateLinkShareDoNotSendPasswordByTalkWithTalkDisabledDoesNotChangeOther(): void {
3515
+        $ocs = $this->mockFormatShare();
3516
+
3517
+        $date = new \DateTime('2000-01-01');
3518
+        $date->setTime(0, 0, 0);
3519
+
3520
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
3521
+        $node->method('getId')
3522
+            ->willReturn(42);
3523
+
3524
+        $share = $this->newShare();
3525
+        $share->setPermissions(Constants::PERMISSION_ALL)
3526
+            ->setSharedBy($this->currentUser)
3527
+            ->setShareType(IShare::TYPE_LINK)
3528
+            ->setPassword('password')
3529
+            ->setSendPasswordByTalk(true)
3530
+            ->setExpirationDate($date)
3531
+            ->setNote('note')
3532
+            ->setLabel('label')
3533
+            ->setHideDownload(true)
3534
+            ->setPermissions(Constants::PERMISSION_ALL)
3535
+            ->setNode($node);
3536
+
3537
+        $node->expects($this->once())
3538
+            ->method('lock')
3539
+            ->with(ILockingProvider::LOCK_SHARED);
3540
+
3541
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3542
+
3543
+        $this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(false);
3544
+
3545
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3546
+            $this->callback(function (IShare $share) use ($date) {
3547
+                return $share->getPermissions() === Constants::PERMISSION_ALL
3548
+                && $share->getPassword() === 'password'
3549
+                && $share->getSendPasswordByTalk() === false
3550
+                && $share->getExpirationDate() === $date
3551
+                && $share->getNote() === 'note'
3552
+                && $share->getLabel() === 'label'
3553
+                && $share->getHideDownload() === true;
3554
+            })
3555
+        )->willReturnArgument(0);
3556
+
3557
+        $this->rootFolder->method('getUserFolder')
3558
+            ->with($this->currentUser)
3559
+            ->willReturn($userFolder);
3560
+
3561
+        $userFolder->method('getById')
3562
+            ->with(42)
3563
+            ->willReturn([$node]);
3564
+
3565
+        $mountPoint = $this->createMock(IMountPoint::class);
3566
+        $node->method('getMountPoint')
3567
+            ->willReturn($mountPoint);
3568
+        $mountPoint->method('getStorageRootId')
3569
+            ->willReturn(42);
3570
+
3571
+        $mountPoint = $this->createMock(IMountPoint::class);
3572
+        $node->method('getMountPoint')
3573
+            ->willReturn($mountPoint);
3574
+        $mountPoint->method('getStorageRootId')
3575
+            ->willReturn(42);
3576
+
3577
+        $expected = new DataResponse([]);
3578
+        $result = $ocs->updateShare(42, null, null, 'false', null, null, null, null, null);
3579
+
3580
+        $this->assertInstanceOf(get_class($expected), $result);
3581
+        $this->assertEquals($expected->getData(), $result->getData());
3582
+    }
3583
+
3584
+    public function testUpdateLinkShareExpireDateDoesNotChangeOther(): void {
3585
+        $ocs = $this->mockFormatShare();
3586
+
3587
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
3588
+        $node->method('getId')
3589
+            ->willReturn(42);
3590
+
3591
+        $share = $this->newShare();
3592
+        $share->setPermissions(Constants::PERMISSION_ALL)
3593
+            ->setSharedBy($this->currentUser)
3594
+            ->setShareType(IShare::TYPE_LINK)
3595
+            ->setPassword('password')
3596
+            ->setSendPasswordByTalk(true)
3597
+            ->setExpirationDate(new \DateTime())
3598
+            ->setNote('note')
3599
+            ->setLabel('label')
3600
+            ->setHideDownload(true)
3601
+            ->setPermissions(Constants::PERMISSION_ALL)
3602
+            ->setNode($node);
3603
+
3604
+        $node->expects($this->once())
3605
+            ->method('lock')
3606
+            ->with(ILockingProvider::LOCK_SHARED);
3607
+
3608
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3609
+
3610
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3611
+            $this->callback(function (IShare $share) {
3612
+                $date = new \DateTime('2010-12-23');
3613
+                $date->setTime(0, 0, 0);
3614
+
3615
+                return $share->getPermissions() === Constants::PERMISSION_ALL
3616
+                && $share->getPassword() === 'password'
3617
+                && $share->getSendPasswordByTalk() === true
3618
+                && $share->getExpirationDate() == $date
3619
+                && $share->getNote() === 'note'
3620
+                && $share->getLabel() === 'label'
3621
+                && $share->getHideDownload() === true;
3622
+            })
3623
+        )->willReturnArgument(0);
3624
+
3625
+        $this->rootFolder->method('getUserFolder')
3626
+            ->with($this->currentUser)
3627
+            ->willReturn($userFolder);
3628
+
3629
+        $userFolder->method('getById')
3630
+            ->with(42)
3631
+            ->willReturn([$node]);
3632
+
3633
+        $mountPoint = $this->createMock(IMountPoint::class);
3634
+        $node->method('getMountPoint')
3635
+            ->willReturn($mountPoint);
3636
+        $mountPoint->method('getStorageRootId')
3637
+            ->willReturn(42);
3638
+
3639
+        $expected = new DataResponse([]);
3640
+        $result = $ocs->updateShare(42, null, null, null, null, '2010-12-23', null, null, null);
3641
+
3642
+        $this->assertInstanceOf(get_class($expected), $result);
3643
+        $this->assertEquals($expected->getData(), $result->getData());
3644
+    }
3645
+
3646
+    public function testUpdateLinkSharePublicUploadDoesNotChangeOther(): void {
3647
+        $ocs = $this->mockFormatShare();
3648
+
3649
+        $date = new \DateTime('2000-01-01');
3650
+
3651
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3652
+        $folder->method('getId')
3653
+            ->willReturn(42);
3654
+
3655
+        $share = Server::get(IManager::class)->newShare();
3656
+        $share->setPermissions(Constants::PERMISSION_ALL)
3657
+            ->setSharedBy($this->currentUser)
3658
+            ->setShareType(IShare::TYPE_LINK)
3659
+            ->setPassword('password')
3660
+            ->setSendPasswordByTalk(true)
3661
+            ->setExpirationDate($date)
3662
+            ->setNote('note')
3663
+            ->setLabel('label')
3664
+            ->setHideDownload(true)
3665
+            ->setPermissions(Constants::PERMISSION_ALL)
3666
+            ->setNode($folder);
3667
+
3668
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3669
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3670
+
3671
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3672
+            $this->callback(function (IShare $share) use ($date) {
3673
+                return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3674
+                && $share->getPassword() === 'password'
3675
+                && $share->getSendPasswordByTalk() === true
3676
+                && $share->getExpirationDate() === $date
3677
+                && $share->getNote() === 'note'
3678
+                && $share->getLabel() === 'label'
3679
+                && $share->getHideDownload() === true;
3680
+            })
3681
+        )->willReturnArgument(0);
3682
+
3683
+        $this->shareManager->method('getSharedWith')
3684
+            ->willReturn([]);
3685
+
3686
+        $this->rootFolder->method('getUserFolder')
3687
+            ->with($this->currentUser)
3688
+            ->willReturn($userFolder);
3689
+
3690
+        $userFolder->method('getById')
3691
+            ->with(42)
3692
+            ->willReturn([$folder]);
3693
+
3694
+        $mountPoint = $this->createMock(IMountPoint::class);
3695
+        $folder->method('getMountPoint')
3696
+            ->willReturn($mountPoint);
3697
+        $mountPoint->method('getStorageRootId')
3698
+            ->willReturn(42);
3699
+
3700
+        $expected = new DataResponse([]);
3701
+        $result = $ocs->updateShare(42, null, null, null, 'true', null, null, null, null);
3702
+
3703
+        $this->assertInstanceOf(get_class($expected), $result);
3704
+        $this->assertEquals($expected->getData(), $result->getData());
3705
+    }
3706
+
3707
+    public function testUpdateLinkSharePermissions(): void {
3708
+        $ocs = $this->mockFormatShare();
3709
+
3710
+        $date = new \DateTime('2000-01-01');
3711
+
3712
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3713
+        $folder->method('getId')
3714
+            ->willReturn(42);
3715
+
3716
+        $share = Server::get(IManager::class)->newShare();
3717
+        $share->setPermissions(Constants::PERMISSION_ALL)
3718
+            ->setSharedBy($this->currentUser)
3719
+            ->setShareType(IShare::TYPE_LINK)
3720
+            ->setPassword('password')
3721
+            ->setSendPasswordByTalk(true)
3722
+            ->setExpirationDate($date)
3723
+            ->setNote('note')
3724
+            ->setLabel('label')
3725
+            ->setHideDownload(true)
3726
+            ->setPermissions(Constants::PERMISSION_ALL)
3727
+            ->setNode($folder);
3728
+
3729
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3730
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3731
+
3732
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3733
+            $this->callback(function (IShare $share) use ($date): bool {
3734
+                return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3735
+                && $share->getPassword() === 'password'
3736
+                && $share->getSendPasswordByTalk() === true
3737
+                && $share->getExpirationDate() === $date
3738
+                && $share->getNote() === 'note'
3739
+                && $share->getLabel() === 'label'
3740
+                && $share->getHideDownload() === true;
3741
+            })
3742
+        )->willReturnArgument(0);
3743
+
3744
+        $this->shareManager->method('getSharedWith')->willReturn([]);
3745
+
3746
+        $this->rootFolder->method('getUserFolder')
3747
+            ->with($this->currentUser)
3748
+            ->willReturn($userFolder);
3749
+
3750
+        $userFolder->method('getById')
3751
+            ->with(42)
3752
+            ->willReturn([$folder]);
3753
+
3754
+        $mountPoint = $this->createMock(IMountPoint::class);
3755
+        $folder->method('getMountPoint')
3756
+            ->willReturn($mountPoint);
3757
+        $mountPoint->method('getStorageRootId')
3758
+            ->willReturn(42);
3759
+
3760
+        $expected = new DataResponse([]);
3761
+        $result = $ocs->updateShare(42, 7, null, null, 'true', null, null, null, null);
3762
+
3763
+        $this->assertInstanceOf(get_class($expected), $result);
3764
+        $this->assertEquals($expected->getData(), $result->getData());
3765
+    }
3766
+
3767
+    public function testUpdateLinkSharePermissionsShare(): void {
3768
+        $ocs = $this->mockFormatShare();
3769
+
3770
+        $date = new \DateTime('2000-01-01');
3771
+
3772
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3773
+        $folder->method('getId')
3774
+            ->willReturn(42);
3775
+
3776
+        $share = Server::get(IManager::class)->newShare();
3777
+        $share->setPermissions(Constants::PERMISSION_ALL)
3778
+            ->setSharedBy($this->currentUser)
3779
+            ->setShareType(IShare::TYPE_LINK)
3780
+            ->setPassword('password')
3781
+            ->setSendPasswordByTalk(true)
3782
+            ->setExpirationDate($date)
3783
+            ->setNote('note')
3784
+            ->setLabel('label')
3785
+            ->setHideDownload(true)
3786
+            ->setPermissions(Constants::PERMISSION_READ)
3787
+            ->setNode($folder);
3788
+
3789
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3790
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3791
+
3792
+        $this->shareManager->expects($this->once())
3793
+            ->method('updateShare')
3794
+            ->with(
3795
+                $this->callback(function (IShare $share) use ($date) {
3796
+                    return $share->getPermissions() === Constants::PERMISSION_ALL
3797
+                        && $share->getPassword() === 'password'
3798
+                        && $share->getSendPasswordByTalk() === true
3799
+                        && $share->getExpirationDate() === $date
3800
+                        && $share->getNote() === 'note'
3801
+                        && $share->getLabel() === 'label'
3802
+                        && $share->getHideDownload() === true;
3803
+                })
3804
+            )->willReturnArgument(0);
3805
+
3806
+        $this->rootFolder->method('getUserFolder')
3807
+            ->with($this->currentUser)
3808
+            ->willReturn($userFolder);
3809
+
3810
+        $userFolder->method('getById')
3811
+            ->with(42)
3812
+            ->willReturn([$folder]);
3813
+
3814
+        $mountPoint = $this->createMock(IMountPoint::class);
3815
+        $folder->method('getMountPoint')
3816
+            ->willReturn($mountPoint);
3817
+        $mountPoint->method('getStorageRootId')
3818
+            ->willReturn(42);
3819
+
3820
+        $this->shareManager->method('getSharedWith')->willReturn([]);
3821
+
3822
+        $expected = new DataResponse([]);
3823
+        $result = $ocs->updateShare(42, Constants::PERMISSION_ALL, null, null, null, null, null, null, null);
3824
+
3825
+        $this->assertInstanceOf(get_class($expected), $result);
3826
+        $this->assertEquals($expected->getData(), $result->getData());
3827
+    }
3828
+
3829
+    public function testUpdateOtherPermissions(): void {
3830
+        $ocs = $this->mockFormatShare();
3831
+
3832
+        [$userFolder, $file] = $this->getNonSharedUserFolder();
3833
+        $file->method('getId')
3834
+            ->willReturn(42);
3835
+
3836
+        $share = Server::get(IManager::class)->newShare();
3837
+        $share->setPermissions(Constants::PERMISSION_ALL)
3838
+            ->setSharedBy($this->currentUser)
3839
+            ->setShareType(IShare::TYPE_USER)
3840
+            ->setNode($file);
3841
+
3842
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3843
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3844
+
3845
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3846
+            $this->callback(function (IShare $share) {
3847
+                return $share->getPermissions() === Constants::PERMISSION_ALL;
3848
+            })
3849
+        )->willReturnArgument(0);
3850
+
3851
+        $this->shareManager->method('getSharedWith')->willReturn([]);
3852
+
3853
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3854
+        $this->rootFolder->method('getUserFolder')
3855
+            ->with($this->currentUser)
3856
+            ->willReturn($userFolder);
3857
+
3858
+        $userFolder->method('getById')
3859
+            ->with(42)
3860
+            ->willReturn([$file]);
3861
+
3862
+        $mountPoint = $this->createMock(IMountPoint::class);
3863
+        $file->method('getMountPoint')
3864
+            ->willReturn($mountPoint);
3865
+        $mountPoint->method('getStorageRootId')
3866
+            ->willReturn(42);
3867
+
3868
+        $expected = new DataResponse([]);
3869
+        $result = $ocs->updateShare(42, 31, null, null, null, null);
3870
+
3871
+        $this->assertInstanceOf(get_class($expected), $result);
3872
+        $this->assertEquals($expected->getData(), $result->getData());
3873
+    }
3874
+
3875
+    public function testUpdateShareCannotIncreasePermissions(): void {
3876
+        $ocs = $this->mockFormatShare();
3877
+
3878
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3879
+        $folder->method('getId')
3880
+            ->willReturn(42);
3881
+
3882
+        $share = Server::get(IManager::class)->newShare();
3883
+        $share
3884
+            ->setId(42)
3885
+            ->setSharedBy($this->currentUser)
3886
+            ->setShareOwner('anotheruser')
3887
+            ->setShareType(IShare::TYPE_GROUP)
3888
+            ->setSharedWith('group1')
3889
+            ->setPermissions(Constants::PERMISSION_READ)
3890
+            ->setNode($folder);
3891
+
3892
+        // note: updateShare will modify the received instance but getSharedWith will reread from the database,
3893
+        // so their values will be different
3894
+        $incomingShare = Server::get(IManager::class)->newShare();
3895
+        $incomingShare
3896
+            ->setId(42)
3897
+            ->setSharedBy($this->currentUser)
3898
+            ->setShareOwner('anotheruser')
3899
+            ->setShareType(IShare::TYPE_GROUP)
3900
+            ->setSharedWith('group1')
3901
+            ->setPermissions(Constants::PERMISSION_READ)
3902
+            ->setNode($folder);
3903
+
3904
+        $this->request
3905
+            ->method('getParam')
3906
+            ->willReturnMap([
3907
+                ['permissions', null, '31'],
3908
+            ]);
3909
+
3910
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3911
+
3912
+        $this->shareManager->expects($this->any())
3913
+            ->method('getSharedWith')
3914
+            ->willReturnMap([
3915
+                ['currentUser', IShare::TYPE_USER, $share->getNode(), -1, 0, []],
3916
+                ['currentUser', IShare::TYPE_GROUP, $share->getNode(), -1, 0, [$incomingShare]],
3917
+                ['currentUser', IShare::TYPE_ROOM, $share->getNode(), -1, 0, []]
3918
+            ]);
3919
+
3920
+        $this->rootFolder->method('getUserFolder')
3921
+            ->with($this->currentUser)
3922
+            ->willReturn($userFolder);
3923
+
3924
+        $userFolder->method('getById')
3925
+            ->with(42)
3926
+            ->willReturn([$folder]);
3927
+        $userFolder->method('getFirstNodeById')
3928
+            ->with(42)
3929
+            ->willReturn($folder);
3930
+
3931
+        $mountPoint = $this->createMock(IMountPoint::class);
3932
+        $folder->method('getMountPoint')
3933
+            ->willReturn($mountPoint);
3934
+        $mountPoint->method('getStorageRootId')
3935
+            ->willReturn(42);
3936
+
3937
+        $this->shareManager->expects($this->once())
3938
+            ->method('updateShare')
3939
+            ->with($share)
3940
+            ->willThrowException(new GenericShareException('Cannot increase permissions of path/file', 'Cannot increase permissions of path/file', 404));
3941
+
3942
+        try {
3943
+            $ocs->updateShare(42, 31);
3944
+            $this->fail();
3945
+        } catch (OCSException $e) {
3946
+            $this->assertEquals('Cannot increase permissions of path/file', $e->getMessage());
3947
+        }
3948
+    }
3949
+
3950
+    public function testUpdateShareCanIncreasePermissionsIfOwner(): void {
3951
+        $ocs = $this->mockFormatShare();
3952
+
3953
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3954
+        $folder->method('getId')
3955
+            ->willReturn(42);
3956
+
3957
+        $share = Server::get(IManager::class)->newShare();
3958
+        $share
3959
+            ->setId(42)
3960
+            ->setSharedBy($this->currentUser)
3961
+            ->setShareOwner($this->currentUser)
3962
+            ->setShareType(IShare::TYPE_GROUP)
3963
+            ->setSharedWith('group1')
3964
+            ->setPermissions(Constants::PERMISSION_READ)
3965
+            ->setNode($folder);
3966
+
3967
+        // note: updateShare will modify the received instance but getSharedWith will reread from the database,
3968
+        // so their values will be different
3969
+        $incomingShare = Server::get(IManager::class)->newShare();
3970
+        $incomingShare
3971
+            ->setId(42)
3972
+            ->setSharedBy($this->currentUser)
3973
+            ->setShareOwner($this->currentUser)
3974
+            ->setShareType(IShare::TYPE_GROUP)
3975
+            ->setSharedWith('group1')
3976
+            ->setPermissions(Constants::PERMISSION_READ)
3977
+            ->setNode($folder);
3978
+
3979
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3980
+
3981
+        $this->shareManager->expects($this->any())
3982
+            ->method('getSharedWith')
3983
+            ->willReturnMap([
3984
+                ['currentUser', IShare::TYPE_USER, $share->getNode(), -1, 0, []],
3985
+                ['currentUser', IShare::TYPE_GROUP, $share->getNode(), -1, 0, [$incomingShare]]
3986
+            ]);
3987
+
3988
+        $this->shareManager->expects($this->once())
3989
+            ->method('updateShare')
3990
+            ->with($share)
3991
+            ->willReturn($share);
3992
+
3993
+        $this->rootFolder->method('getUserFolder')
3994
+            ->with($this->currentUser)
3995
+            ->willReturn($userFolder);
3996
+
3997
+        $userFolder->method('getById')
3998
+            ->with(42)
3999
+            ->willReturn([$folder]);
4000
+
4001
+        $mountPoint = $this->createMock(IMountPoint::class);
4002
+        $folder->method('getMountPoint')
4003
+            ->willReturn($mountPoint);
4004
+        $mountPoint->method('getStorageRootId')
4005
+            ->willReturn(42);
4006
+
4007
+        $result = $ocs->updateShare(42, 31);
4008
+        $this->assertInstanceOf(DataResponse::class, $result);
4009
+    }
4010
+
4011
+    public function testUpdateShareOwnerless(): void {
4012
+        $ocs = $this->mockFormatShare();
4013
+
4014
+        $mount = $this->createMock(IShareOwnerlessMount::class);
4015
+
4016
+        $file = $this->createMock(File::class);
4017
+        $file
4018
+            ->expects($this->exactly(2))
4019
+            ->method('getPermissions')
4020
+            ->willReturn(Constants::PERMISSION_SHARE);
4021
+        $file
4022
+            ->expects($this->once())
4023
+            ->method('getMountPoint')
4024
+            ->willReturn($mount);
4025
+
4026
+        $userFolder = $this->createMock(Folder::class);
4027
+        $userFolder->method('getById')
4028
+            ->with(2)
4029
+            ->willReturn([$file]);
4030
+        $userFolder->method('getFirstNodeById')
4031
+            ->with(2)
4032
+            ->willReturn($file);
4033
+
4034
+        $this->rootFolder
4035
+            ->method('getUserFolder')
4036
+            ->with($this->currentUser)
4037
+            ->willReturn($userFolder);
4038
+
4039
+        $share = $this->createMock(IShare::class);
4040
+        $share
4041
+            ->expects($this->once())
4042
+            ->method('getNode')
4043
+            ->willReturn($file);
4044
+        $share
4045
+            ->expects($this->exactly(2))
4046
+            ->method('getNodeId')
4047
+            ->willReturn(2);
4048
+        $share
4049
+            ->expects($this->exactly(2))
4050
+            ->method('getPermissions')
4051
+            ->willReturn(Constants::PERMISSION_SHARE);
4052
+
4053
+        $this->shareManager
4054
+            ->expects($this->once())
4055
+            ->method('getShareById')
4056
+            ->with('ocinternal:1', $this->currentUser)
4057
+            ->willReturn($share);
4058
+
4059
+        $this->shareManager
4060
+            ->expects($this->once())
4061
+            ->method('updateShare')
4062
+            ->with($share)
4063
+            ->willReturn($share);
4064
+
4065
+        $result = $ocs->updateShare(1, Constants::PERMISSION_ALL);
4066
+        $this->assertInstanceOf(DataResponse::class, $result);
4067
+    }
4068
+
4069
+    public static function dataFormatShare(): array {
4070
+        $owner = ['getDisplayName' => 'ownerDN'];
4071
+        $initiator = ['getDisplayName' => 'initiatorDN'];
4072
+        $recipient = [
4073
+            'getDisplayName' => 'recipientDN',
4074
+            'getSystemEMailAddress' => 'recipient'
4075
+        ];
4076
+
4077
+        $folder = [
4078
+            'class' => Folder::class,
4079
+            'mimeType' => 'myFolderMimeType',
4080
+            'path' => 'folder',
4081
+            'id' => 2,
4082
+        ];
4083
+        $file = [
4084
+            'class' => File::class,
4085
+            'mimeType' => 'myMimeType',
4086
+            'path' => 'file',
4087
+            'id' => 3,
4088
+        ];
4089
+        $fileWithPreview = [
4090
+            'class' => File::class,
4091
+            'mimeType' => 'mimeWithPreview',
4092
+            'path' => 'fileWithPreview',
4093
+            'id' => 4,
4094
+        ];
4095
+
4096
+        $result = [];
4097
+
4098
+        $share = [
4099
+            'type' => IShare::TYPE_USER,
4100
+            'owner' => 'owner',
4101
+            'sharedWith' => 'recipient',
4102
+            'attributes' => [
4103
+                'scope' => 'permissions',
4104
+                'key' => 'download',
4105
+                'value' => true
4106
+            ],
4107
+            'node' => $file,
4108
+            'note' => 'personal note',
4109
+        ];
4110
+
4111
+        // User backend down
4112
+        $result[] = [
4113
+            [
4114
+                'id' => '42',
4115
+                'share_type' => IShare::TYPE_USER,
4116
+                'uid_owner' => 'initiator',
4117
+                'displayname_owner' => 'initiator',
4118
+                'permissions' => 1,
4119
+                'stime' => 946684862,
4120
+                'parent' => null,
4121
+                'expiration' => null,
4122
+                'token' => null,
4123
+                'uid_file_owner' => 'owner',
4124
+                'displayname_file_owner' => 'owner',
4125
+                'path' => 'file',
4126
+                'item_type' => 'file',
4127
+                'storage_id' => 'storageId',
4128
+                'storage' => 100,
4129
+                'item_source' => 3,
4130
+                'file_source' => 3,
4131
+                'file_parent' => 1,
4132
+                'file_target' => 'myTarget',
4133
+                'share_with' => 'recipient',
4134
+                'share_with_displayname' => 'recipient',
4135
+                'share_with_displayname_unique' => 'recipient',
4136
+                'note' => 'personal note',
4137
+                'label' => '',
4138
+                'mail_send' => 0,
4139
+                'mimetype' => 'myMimeType',
4140
+                'has_preview' => false,
4141
+                'hide_download' => 0,
4142
+                'can_edit' => false,
4143
+                'can_delete' => false,
4144
+                'item_size' => 123456,
4145
+                'item_mtime' => 1234567890,
4146
+                'is-mount-root' => false,
4147
+                'mount-type' => '',
4148
+                'attributes' => '[{"scope":"permissions","key":"download","value":true}]',
4149
+                'item_permissions' => 1,
4150
+            ],
4151
+            $share,
4152
+            [], false
4153
+        ];
4154
+        // User backend up
4155
+        $result[] = [
4156
+            [
4157
+                'id' => '42',
4158
+                'share_type' => IShare::TYPE_USER,
4159
+                'uid_owner' => 'initiator',
4160
+                'displayname_owner' => 'initiatorDN',
4161
+                'permissions' => 1,
4162
+                'stime' => 946684862,
4163
+                'parent' => null,
4164
+                'expiration' => null,
4165
+                'token' => null,
4166
+                'uid_file_owner' => 'owner',
4167
+                'displayname_file_owner' => 'ownerDN',
4168
+                'note' => 'personal note',
4169
+                'label' => '',
4170
+                'path' => 'file',
4171
+                'item_type' => 'file',
4172
+                'storage_id' => 'storageId',
4173
+                'storage' => 100,
4174
+                'item_source' => 3,
4175
+                'file_source' => 3,
4176
+                'file_parent' => 1,
4177
+                'file_target' => 'myTarget',
4178
+                'share_with' => 'recipient',
4179
+                'share_with_displayname' => 'recipientDN',
4180
+                'share_with_displayname_unique' => 'recipient',
4181
+                'mail_send' => 0,
4182
+                'mimetype' => 'myMimeType',
4183
+                'has_preview' => false,
4184
+                'hide_download' => 0,
4185
+                'can_edit' => false,
4186
+                'can_delete' => false,
4187
+                'item_size' => 123456,
4188
+                'item_mtime' => 1234567890,
4189
+                'is-mount-root' => false,
4190
+                'mount-type' => '',
4191
+                'attributes' => '[{"scope":"permissions","key":"download","value":true}]',
4192
+                'item_permissions' => 1,
4193
+            ], $share, [
4194
+                ['owner', $owner],
4195
+                ['initiator', $initiator],
4196
+                ['recipient', $recipient],
4197
+            ], false
4198
+        ];
4199
+
4200
+        // Same but no attributes
4201
+        $share = [
4202
+            'type' => IShare::TYPE_USER,
4203
+            'owner' => 'owner',
4204
+            'sharedWith' => 'recipient',
4205
+            'node' => $file,
4206
+            'note' => 'personal note',
4207
+        ];
4208
+
4209
+        // User backend down
4210
+        $result[] = [
4211
+            [
4212
+                'id' => '42',
4213
+                'share_type' => IShare::TYPE_USER,
4214
+                'uid_owner' => 'initiator',
4215
+                'displayname_owner' => 'initiator',
4216
+                'permissions' => 1,
4217
+                'attributes' => null,
4218
+                'stime' => 946684862,
4219
+                'parent' => null,
4220
+                'expiration' => null,
4221
+                'token' => null,
4222
+                'uid_file_owner' => 'owner',
4223
+                'displayname_file_owner' => 'owner',
4224
+                'note' => 'personal note',
4225
+                'label' => '',
4226
+                'path' => 'file',
4227
+                'item_type' => 'file',
4228
+                'storage_id' => 'storageId',
4229
+                'storage' => 100,
4230
+                'item_source' => 3,
4231
+                'file_source' => 3,
4232
+                'file_parent' => 1,
4233
+                'file_target' => 'myTarget',
4234
+                'share_with' => 'recipient',
4235
+                'share_with_displayname' => 'recipient',
4236
+                'share_with_displayname_unique' => 'recipient',
4237
+                'mail_send' => 0,
4238
+                'mimetype' => 'myMimeType',
4239
+                'has_preview' => false,
4240
+                'hide_download' => 0,
4241
+                'can_edit' => false,
4242
+                'can_delete' => false,
4243
+                'item_size' => 123456,
4244
+                'item_mtime' => 1234567890,
4245
+                'is-mount-root' => false,
4246
+                'mount-type' => '',
4247
+                'attributes' => null,
4248
+                'item_permissions' => 1,
4249
+            ], $share, [], false
4250
+        ];
4251
+
4252
+        $share['owner'] = 'currentUser';
4253
+
4254
+        // User backend down
4255
+        $result[] = [
4256
+            [
4257
+                'id' => '42',
4258
+                'share_type' => IShare::TYPE_USER,
4259
+                'uid_owner' => 'initiator',
4260
+                'displayname_owner' => 'initiator',
4261
+                'permissions' => 1,
4262
+                'attributes' => null,
4263
+                'stime' => 946684862,
4264
+                'parent' => null,
4265
+                'expiration' => null,
4266
+                'token' => null,
4267
+                'uid_file_owner' => 'currentUser',
4268
+                'displayname_file_owner' => 'currentUser',
4269
+                'note' => 'personal note',
4270
+                'label' => '',
4271
+                'path' => 'file',
4272
+                'item_type' => 'file',
4273
+                'storage_id' => 'storageId',
4274
+                'storage' => 100,
4275
+                'item_source' => 3,
4276
+                'file_source' => 3,
4277
+                'file_parent' => 1,
4278
+                'file_target' => 'myTarget',
4279
+                'share_with' => 'recipient',
4280
+                'share_with_displayname' => 'recipient',
4281
+                'share_with_displayname_unique' => 'recipient',
4282
+                'mail_send' => 0,
4283
+                'mimetype' => 'myMimeType',
4284
+                'has_preview' => false,
4285
+                'hide_download' => 0,
4286
+                'can_edit' => true,
4287
+                'can_delete' => true,
4288
+                'item_size' => 123456,
4289
+                'item_mtime' => 1234567890,
4290
+                'is-mount-root' => false,
4291
+                'mount-type' => '',
4292
+                'attributes' => null,
4293
+                'item_permissions' => 11,
4294
+            ], $share, [], false
4295
+        ];
4296
+
4297
+        // with existing group
4298
+        $share = [
4299
+            'type' => IShare::TYPE_GROUP,
4300
+            'owner' => 'owner',
4301
+            'sharedWith' => 'recipientGroup',
4302
+            'node' => $file,
4303
+            'note' => 'personal note',
4304
+        ];
4305
+
4306
+        $result[] = [
4307
+            [
4308
+                'id' => '42',
4309
+                'share_type' => IShare::TYPE_GROUP,
4310
+                'uid_owner' => 'initiator',
4311
+                'displayname_owner' => 'initiator',
4312
+                'permissions' => 1,
4313
+                'attributes' => null,
4314
+                'stime' => 946684862,
4315
+                'parent' => null,
4316
+                'expiration' => null,
4317
+                'token' => null,
4318
+                'uid_file_owner' => 'owner',
4319
+                'displayname_file_owner' => 'owner',
4320
+                'note' => 'personal note',
4321
+                'label' => '',
4322
+                'path' => 'file',
4323
+                'item_type' => 'file',
4324
+                'storage_id' => 'storageId',
4325
+                'storage' => 100,
4326
+                'item_source' => 3,
4327
+                'file_source' => 3,
4328
+                'file_parent' => 1,
4329
+                'file_target' => 'myTarget',
4330
+                'share_with' => 'recipientGroup',
4331
+                'share_with_displayname' => 'recipientGroupDisplayName',
4332
+                'mail_send' => 0,
4333
+                'mimetype' => 'myMimeType',
4334
+                'has_preview' => false,
4335
+                'hide_download' => 0,
4336
+                'can_edit' => false,
4337
+                'can_delete' => false,
4338
+                'item_size' => 123456,
4339
+                'item_mtime' => 1234567890,
4340
+                'is-mount-root' => false,
4341
+                'mount-type' => '',
4342
+                'attributes' => null,
4343
+                'item_permissions' => 1,
4344
+            ], $share, [], false
4345
+        ];
4346
+
4347
+        // with unknown group / no group backend
4348
+        $share['sharedWith'] = 'recipientGroup2';
4349
+
4350
+        $result[] = [
4351
+            [
4352
+                'id' => '42',
4353
+                'share_type' => IShare::TYPE_GROUP,
4354
+                'uid_owner' => 'initiator',
4355
+                'displayname_owner' => 'initiator',
4356
+                'permissions' => 1,
4357
+                'stime' => 946684862,
4358
+                'parent' => null,
4359
+                'expiration' => null,
4360
+                'token' => null,
4361
+                'uid_file_owner' => 'owner',
4362
+                'displayname_file_owner' => 'owner',
4363
+                'note' => 'personal note',
4364
+                'label' => '',
4365
+                'path' => 'file',
4366
+                'item_type' => 'file',
4367
+                'storage_id' => 'storageId',
4368
+                'storage' => 100,
4369
+                'item_source' => 3,
4370
+                'file_source' => 3,
4371
+                'file_parent' => 1,
4372
+                'file_target' => 'myTarget',
4373
+                'share_with' => 'recipientGroup2',
4374
+                'share_with_displayname' => 'recipientGroup2',
4375
+                'mail_send' => 0,
4376
+                'mimetype' => 'myMimeType',
4377
+                'has_preview' => false,
4378
+                'hide_download' => 0,
4379
+                'can_edit' => false,
4380
+                'can_delete' => false,
4381
+                'item_size' => 123456,
4382
+                'item_mtime' => 1234567890,
4383
+                'is-mount-root' => false,
4384
+                'mount-type' => '',
4385
+                'attributes' => null,
4386
+                'item_permissions' => 1,
4387
+            ], $share, [], false
4388
+        ];
4389
+
4390
+        $share = [
4391
+            'type' => IShare::TYPE_LINK,
4392
+            'owner' => 'owner',
4393
+            'node' => $file,
4394
+            'note' => 'personal note',
4395
+            'password' => 'mypassword',
4396
+            'expirationDate' => new \DateTime('2001-01-02T00:00:00'),
4397
+            'token' => 'myToken',
4398
+            'label' => 'new link share',
4399
+        ];
4400
+
4401
+        $result[] = [
4402
+            [
4403
+                'id' => '42',
4404
+                'share_type' => IShare::TYPE_LINK,
4405
+                'uid_owner' => 'initiator',
4406
+                'displayname_owner' => 'initiator',
4407
+                'permissions' => 1,
4408
+                'attributes' => null,
4409
+                'stime' => 946684862,
4410
+                'parent' => null,
4411
+                'expiration' => '2001-01-02 00:00:00',
4412
+                'token' => 'myToken',
4413
+                'uid_file_owner' => 'owner',
4414
+                'displayname_file_owner' => 'owner',
4415
+                'note' => 'personal note',
4416
+                'label' => 'new link share',
4417
+                'path' => 'file',
4418
+                'item_type' => 'file',
4419
+                'storage_id' => 'storageId',
4420
+                'storage' => 100,
4421
+                'item_source' => 3,
4422
+                'file_source' => 3,
4423
+                'file_parent' => 1,
4424
+                'file_target' => 'myTarget',
4425
+                'password' => 'mypassword',
4426
+                'share_with' => 'mypassword',
4427
+                'share_with_displayname' => '(Shared link)',
4428
+                'send_password_by_talk' => false,
4429
+                'mail_send' => 0,
4430
+                'url' => 'myLink',
4431
+                'mimetype' => 'myMimeType',
4432
+                'has_preview' => false,
4433
+                'hide_download' => 0,
4434
+                'can_edit' => false,
4435
+                'can_delete' => false,
4436
+                'item_size' => 123456,
4437
+                'item_mtime' => 1234567890,
4438
+                'is-mount-root' => false,
4439
+                'mount-type' => '',
4440
+                'attributes' => null,
4441
+                'item_permissions' => 1,
4442
+            ], $share, [], false
4443
+        ];
4444
+
4445
+        $share['sendPasswordByTalk'] = true;
4446
+
4447
+        $result[] = [
4448
+            [
4449
+                'id' => '42',
4450
+                'share_type' => IShare::TYPE_LINK,
4451
+                'uid_owner' => 'initiator',
4452
+                'displayname_owner' => 'initiator',
4453
+                'permissions' => 1,
4454
+                'stime' => 946684862,
4455
+                'parent' => null,
4456
+                'expiration' => '2001-01-02 00:00:00',
4457
+                'token' => 'myToken',
4458
+                'uid_file_owner' => 'owner',
4459
+                'displayname_file_owner' => 'owner',
4460
+                'note' => 'personal note',
4461
+                'label' => 'new link share',
4462
+                'path' => 'file',
4463
+                'item_type' => 'file',
4464
+                'storage_id' => 'storageId',
4465
+                'storage' => 100,
4466
+                'item_source' => 3,
4467
+                'file_source' => 3,
4468
+                'file_parent' => 1,
4469
+                'file_target' => 'myTarget',
4470
+                'password' => 'mypassword',
4471
+                'share_with' => 'mypassword',
4472
+                'share_with_displayname' => '(Shared link)',
4473
+                'send_password_by_talk' => true,
4474
+                'mail_send' => 0,
4475
+                'url' => 'myLink',
4476
+                'mimetype' => 'myMimeType',
4477
+                'has_preview' => false,
4478
+                'hide_download' => 0,
4479
+                'can_edit' => false,
4480
+                'can_delete' => false,
4481
+                'item_size' => 123456,
4482
+                'item_mtime' => 1234567890,
4483
+                'is-mount-root' => false,
4484
+                'mount-type' => '',
4485
+                'attributes' => null,
4486
+                'item_permissions' => 1,
4487
+            ], $share, [], false
4488
+        ];
4489
+
4490
+        $share = [
4491
+            'type' => IShare::TYPE_REMOTE,
4492
+            'owner' => 'owner',
4493
+            'sharedWith' => '[email protected]',
4494
+            'node' => $folder,
4495
+            'note' => 'personal note',
4496
+            'expirationDate' => new \DateTime('2001-02-03T04:05:06'),
4497
+        ];
4498
+
4499
+        $result[] = [
4500
+            [
4501
+                'id' => '42',
4502
+                'share_type' => IShare::TYPE_REMOTE,
4503
+                'uid_owner' => 'initiator',
4504
+                'displayname_owner' => 'initiator',
4505
+                'permissions' => 1,
4506
+                'stime' => 946684862,
4507
+                'parent' => null,
4508
+                'expiration' => '2001-02-03 00:00:00',
4509
+                'token' => null,
4510
+                'uid_file_owner' => 'owner',
4511
+                'displayname_file_owner' => 'owner',
4512
+                'note' => 'personal note',
4513
+                'label' => '',
4514
+                'path' => 'folder',
4515
+                'item_type' => 'folder',
4516
+                'storage_id' => 'storageId',
4517
+                'storage' => 100,
4518
+                'item_source' => 2,
4519
+                'file_source' => 2,
4520
+                'file_parent' => 1,
4521
+                'file_target' => 'myTarget',
4522
+                'share_with' => '[email protected]',
4523
+                'share_with_displayname' => 'foobar',
4524
+                'mail_send' => 0,
4525
+                'mimetype' => 'myFolderMimeType',
4526
+                'has_preview' => false,
4527
+                'hide_download' => 0,
4528
+                'can_edit' => false,
4529
+                'can_delete' => false,
4530
+                'item_size' => 123456,
4531
+                'item_mtime' => 1234567890,
4532
+                'is-mount-root' => false,
4533
+                'mount-type' => '',
4534
+                'attributes' => null,
4535
+                'item_permissions' => 1,
4536
+                'is_trusted_server' => false,
4537
+            ], $share, [], false
4538
+        ];
4539
+
4540
+        $share = [
4541
+            'type' => IShare::TYPE_REMOTE_GROUP,
4542
+            'owner' => 'owner',
4543
+            'sharedWith' => '[email protected]',
4544
+            'node' => $folder,
4545
+            'note' => 'personal note',
4546
+            'expirationDate' => new \DateTime('2001-02-03T04:05:06'),
4547
+        ];
4548
+
4549
+        $result[] = [
4550
+            [
4551
+                'id' => '42',
4552
+                'share_type' => IShare::TYPE_REMOTE_GROUP,
4553
+                'uid_owner' => 'initiator',
4554
+                'displayname_owner' => 'initiator',
4555
+                'permissions' => 1,
4556
+                'stime' => 946684862,
4557
+                'parent' => null,
4558
+                'expiration' => '2001-02-03 00:00:00',
4559
+                'token' => null,
4560
+                'uid_file_owner' => 'owner',
4561
+                'displayname_file_owner' => 'owner',
4562
+                'note' => 'personal note',
4563
+                'label' => '',
4564
+                'path' => 'folder',
4565
+                'item_type' => 'folder',
4566
+                'storage_id' => 'storageId',
4567
+                'storage' => 100,
4568
+                'item_source' => 2,
4569
+                'file_source' => 2,
4570
+                'file_parent' => 1,
4571
+                'file_target' => 'myTarget',
4572
+                'share_with' => '[email protected]',
4573
+                'share_with_displayname' => 'foobar',
4574
+                'mail_send' => 0,
4575
+                'mimetype' => 'myFolderMimeType',
4576
+                'has_preview' => false,
4577
+                'hide_download' => 0,
4578
+                'can_edit' => false,
4579
+                'can_delete' => false,
4580
+                'item_size' => 123456,
4581
+                'item_mtime' => 1234567890,
4582
+                'is-mount-root' => false,
4583
+                'mount-type' => '',
4584
+                'attributes' => null,
4585
+                'item_permissions' => 1,
4586
+                'is_trusted_server' => false,
4587
+            ], $share, [], false
4588
+        ];
4589
+
4590
+        // Circle with id, display name and avatar set by the Circles app
4591
+        $share = [
4592
+            'type' => IShare::TYPE_CIRCLE,
4593
+            'owner' => 'owner',
4594
+            'sharedWith' => 'Circle (Public circle, circleOwner) [4815162342]',
4595
+            'sharedWithDisplayName' => 'The display name',
4596
+            'sharedWithAvatar' => 'path/to/the/avatar',
4597
+            'node' => $folder,
4598
+        ];
4599
+
4600
+        $result[] = [
4601
+            [
4602
+                'id' => '42',
4603
+                'share_type' => IShare::TYPE_CIRCLE,
4604
+                'uid_owner' => 'initiator',
4605
+                'displayname_owner' => 'initiator',
4606
+                'permissions' => 1,
4607
+                'attributes' => null,
4608
+                'stime' => 946684862,
4609
+                'parent' => null,
4610
+                'expiration' => null,
4611
+                'token' => null,
4612
+                'uid_file_owner' => 'owner',
4613
+                'displayname_file_owner' => 'owner',
4614
+                'note' => '',
4615
+                'label' => '',
4616
+                'path' => 'folder',
4617
+                'item_type' => 'folder',
4618
+                'storage_id' => 'storageId',
4619
+                'storage' => 100,
4620
+                'item_source' => 2,
4621
+                'file_source' => 2,
4622
+                'file_parent' => 1,
4623
+                'file_target' => 'myTarget',
4624
+                'share_with' => '4815162342',
4625
+                'share_with_displayname' => 'The display name',
4626
+                'share_with_avatar' => 'path/to/the/avatar',
4627
+                'mail_send' => 0,
4628
+                'mimetype' => 'myFolderMimeType',
4629
+                'has_preview' => false,
4630
+                'hide_download' => 0,
4631
+                'can_edit' => false,
4632
+                'can_delete' => false,
4633
+                'item_size' => 123456,
4634
+                'item_mtime' => 1234567890,
4635
+                'is-mount-root' => false,
4636
+                'mount-type' => '',
4637
+                'attributes' => null,
4638
+                'item_permissions' => 1,
4639
+            ], $share, [], false
4640
+        ];
4641
+
4642
+        // Circle with id set by the Circles app
4643
+        $share = [
4644
+            'type' => IShare::TYPE_CIRCLE,
4645
+            'owner' => 'owner',
4646
+            'sharedWith' => 'Circle (Public circle, circleOwner) [4815162342]',
4647
+            'node' => $folder,
4648
+        ];
4649
+
4650
+        $result[] = [
4651
+            [
4652
+                'id' => '42',
4653
+                'share_type' => IShare::TYPE_CIRCLE,
4654
+                'uid_owner' => 'initiator',
4655
+                'displayname_owner' => 'initiator',
4656
+                'permissions' => 1,
4657
+                'stime' => 946684862,
4658
+                'parent' => null,
4659
+                'expiration' => null,
4660
+                'token' => null,
4661
+                'uid_file_owner' => 'owner',
4662
+                'displayname_file_owner' => 'owner',
4663
+                'note' => '',
4664
+                'label' => '',
4665
+                'path' => 'folder',
4666
+                'item_type' => 'folder',
4667
+                'storage_id' => 'storageId',
4668
+                'storage' => 100,
4669
+                'item_source' => 2,
4670
+                'file_source' => 2,
4671
+                'file_parent' => 1,
4672
+                'file_target' => 'myTarget',
4673
+                'share_with' => '4815162342',
4674
+                'share_with_displayname' => 'Circle (Public circle, circleOwner)',
4675
+                'share_with_avatar' => '',
4676
+                'mail_send' => 0,
4677
+                'mimetype' => 'myFolderMimeType',
4678
+                'has_preview' => false,
4679
+                'hide_download' => 0,
4680
+                'can_edit' => false,
4681
+                'can_delete' => false,
4682
+                'item_size' => 123456,
4683
+                'item_mtime' => 1234567890,
4684
+                'is-mount-root' => false,
4685
+                'mount-type' => '',
4686
+                'attributes' => null,
4687
+                'item_permissions' => 1,
4688
+            ], $share, [], false
4689
+        ];
4690
+
4691
+        // Circle with id not set by the Circles app
4692
+        $share = [
4693
+            'type' => IShare::TYPE_CIRCLE,
4694
+            'owner' => 'owner',
4695
+            'sharedWith' => 'Circle (Public circle, circleOwner)',
4696
+            'node' => $folder,
4697
+        ];
4698
+
4699
+        $result[] = [
4700
+            [
4701
+                'id' => '42',
4702
+                'share_type' => IShare::TYPE_CIRCLE,
4703
+                'uid_owner' => 'initiator',
4704
+                'displayname_owner' => 'initiator',
4705
+                'permissions' => 1,
4706
+                'stime' => 946684862,
4707
+                'parent' => null,
4708
+                'expiration' => null,
4709
+                'token' => null,
4710
+                'uid_file_owner' => 'owner',
4711
+                'displayname_file_owner' => 'owner',
4712
+                'note' => '',
4713
+                'label' => '',
4714
+                'path' => 'folder',
4715
+                'item_type' => 'folder',
4716
+                'storage_id' => 'storageId',
4717
+                'storage' => 100,
4718
+                'item_source' => 2,
4719
+                'file_source' => 2,
4720
+                'file_parent' => 1,
4721
+                'file_target' => 'myTarget',
4722
+                'share_with' => 'Circle',
4723
+                'share_with_displayname' => 'Circle (Public circle, circleOwner)',
4724
+                'share_with_avatar' => '',
4725
+                'mail_send' => 0,
4726
+                'mimetype' => 'myFolderMimeType',
4727
+                'has_preview' => false,
4728
+                'hide_download' => 0,
4729
+                'can_edit' => false,
4730
+                'can_delete' => false,
4731
+                'item_size' => 123456,
4732
+                'item_mtime' => 1234567890,
4733
+                'is-mount-root' => false,
4734
+                'mount-type' => '',
4735
+                'attributes' => null,
4736
+                'item_permissions' => 1,
4737
+            ], $share, [], false
4738
+        ];
4739
+
4740
+        // No node
4741
+        $share = [
4742
+            'type' => IShare::TYPE_USER,
4743
+            'owner' => 'owner',
4744
+            'sharedWith' => 'recipient',
4745
+            'note' => 'personal note',
4746
+        ];
4747
+
4748
+        $result[] = [
4749
+            [], $share, [], true
4750
+        ];
4751
+
4752
+        $share = [
4753
+            'type' => IShare::TYPE_EMAIL,
4754
+            'owner' => 'owner',
4755
+            'sharedWith' => '[email protected]',
4756
+            'node' => $folder,
4757
+            'password' => 'password',
4758
+        ];
4759
+
4760
+        $result[] = [
4761
+            [
4762
+                'id' => '42',
4763
+                'share_type' => IShare::TYPE_EMAIL,
4764
+                'uid_owner' => 'initiator',
4765
+                'displayname_owner' => 'initiator',
4766
+                'permissions' => 1,
4767
+                'stime' => 946684862,
4768
+                'parent' => null,
4769
+                'expiration' => null,
4770
+                'token' => null,
4771
+                'uid_file_owner' => 'owner',
4772
+                'displayname_file_owner' => 'owner',
4773
+                'note' => '',
4774
+                'label' => '',
4775
+                'path' => 'folder',
4776
+                'item_type' => 'folder',
4777
+                'storage_id' => 'storageId',
4778
+                'storage' => 100,
4779
+                'item_source' => 2,
4780
+                'file_source' => 2,
4781
+                'file_parent' => 1,
4782
+                'file_target' => 'myTarget',
4783
+                'share_with' => '[email protected]',
4784
+                'share_with_displayname' => 'mail display name',
4785
+                'mail_send' => 0,
4786
+                'mimetype' => 'myFolderMimeType',
4787
+                'has_preview' => false,
4788
+                'password' => 'password',
4789
+                'send_password_by_talk' => false,
4790
+                'hide_download' => 0,
4791
+                'can_edit' => false,
4792
+                'can_delete' => false,
4793
+                'password_expiration_time' => null,
4794
+                'item_size' => 123456,
4795
+                'item_mtime' => 1234567890,
4796
+                'is-mount-root' => false,
4797
+                'mount-type' => '',
4798
+                'attributes' => null,
4799
+                'item_permissions' => 1,
4800
+            ], $share, [], false
4801
+        ];
4802
+
4803
+        $share['sendPasswordByTalk'] = true;
4804
+
4805
+        $result[] = [
4806
+            [
4807
+                'id' => '42',
4808
+                'share_type' => IShare::TYPE_EMAIL,
4809
+                'uid_owner' => 'initiator',
4810
+                'displayname_owner' => 'initiator',
4811
+                'permissions' => 1,
4812
+                'stime' => 946684862,
4813
+                'parent' => null,
4814
+                'expiration' => null,
4815
+                'token' => null,
4816
+                'uid_file_owner' => 'owner',
4817
+                'displayname_file_owner' => 'owner',
4818
+                'note' => '',
4819
+                'label' => '',
4820
+                'path' => 'folder',
4821
+                'item_type' => 'folder',
4822
+                'storage_id' => 'storageId',
4823
+                'storage' => 100,
4824
+                'item_source' => 2,
4825
+                'file_source' => 2,
4826
+                'file_parent' => 1,
4827
+                'file_target' => 'myTarget',
4828
+                'share_with' => '[email protected]',
4829
+                'share_with_displayname' => 'mail display name',
4830
+                'mail_send' => 0,
4831
+                'mimetype' => 'myFolderMimeType',
4832
+                'has_preview' => false,
4833
+                'password' => 'password',
4834
+                'send_password_by_talk' => true,
4835
+                'hide_download' => 0,
4836
+                'can_edit' => false,
4837
+                'can_delete' => false,
4838
+                'password_expiration_time' => null,
4839
+                'item_size' => 123456,
4840
+                'item_mtime' => 1234567890,
4841
+                'is-mount-root' => false,
4842
+                'mount-type' => '',
4843
+                'attributes' => null,
4844
+                'item_permissions' => 1,
4845
+            ], $share, [], false
4846
+        ];
4847
+
4848
+        // Preview is available
4849
+        $share = [
4850
+            'type' => IShare::TYPE_USER,
4851
+            'owner' => 'currentUser',
4852
+            'sharedWith' => 'recipient',
4853
+            'node' => $fileWithPreview,
4854
+            'note' => 'personal note',
4855
+        ];
4856
+
4857
+        $result[] = [
4858
+            [
4859
+                'id' => '42',
4860
+                'share_type' => IShare::TYPE_USER,
4861
+                'uid_owner' => 'initiator',
4862
+                'displayname_owner' => 'initiator',
4863
+                'permissions' => 1,
4864
+                'stime' => 946684862,
4865
+                'parent' => null,
4866
+                'expiration' => null,
4867
+                'token' => null,
4868
+                'uid_file_owner' => 'currentUser',
4869
+                'displayname_file_owner' => 'currentUser',
4870
+                'note' => 'personal note',
4871
+                'label' => '',
4872
+                'path' => 'fileWithPreview',
4873
+                'item_type' => 'file',
4874
+                'storage_id' => 'storageId',
4875
+                'storage' => 100,
4876
+                'item_source' => 4,
4877
+                'file_source' => 4,
4878
+                'file_parent' => 1,
4879
+                'file_target' => 'myTarget',
4880
+                'share_with' => 'recipient',
4881
+                'share_with_displayname' => 'recipient',
4882
+                'share_with_displayname_unique' => 'recipient',
4883
+                'mail_send' => 0,
4884
+                'mimetype' => 'mimeWithPreview',
4885
+                'has_preview' => true,
4886
+                'hide_download' => 0,
4887
+                'can_edit' => true,
4888
+                'can_delete' => true,
4889
+                'item_size' => 123456,
4890
+                'item_mtime' => 1234567890,
4891
+                'is-mount-root' => false,
4892
+                'mount-type' => '',
4893
+                'attributes' => null,
4894
+                'item_permissions' => 11,
4895
+            ], $share, [], false
4896
+        ];
4897
+
4898
+        return $result;
4899
+    }
4900
+
4901
+    #[DataProvider('dataFormatShare')]
4902
+    public function testFormatShare(
4903
+        array $expects,
4904
+        array $shareParams,
4905
+        array $users,
4906
+        bool $exception,
4907
+    ): void {
4908
+        $users = array_map(
4909
+            function ($user) {
4910
+                $mock = $this->createMock(IUser::class);
4911
+                foreach ($user[1] as $method => $return) {
4912
+                    $mock->method($method)->willReturn($return);
4913
+                }
4914
+                return [$user[0],$mock];
4915
+            },
4916
+            $users
4917
+        );
4918
+
4919
+        $share = Server::get(IManager::class)->newShare();
4920
+        $share->setShareType($shareParams['type'])
4921
+            ->setSharedBy('initiator')
4922
+            ->setShareOwner($shareParams['owner'])
4923
+            ->setPermissions(Constants::PERMISSION_READ)
4924
+            ->setShareTime(new \DateTime('2000-01-01T00:01:02'))
4925
+            ->setTarget('myTarget')
4926
+            ->setId(42);
4927
+        if (isset($shareParams['sharedWith'])) {
4928
+            $share->setSharedWith($shareParams['sharedWith']);
4929
+        }
4930
+        if (isset($shareParams['sharedWithDisplayName'])) {
4931
+            $share->setSharedWithDisplayName($shareParams['sharedWithDisplayName']);
4932
+        }
4933
+        if (isset($shareParams['sharedWithAvatar'])) {
4934
+            $share->setSharedWithAvatar($shareParams['sharedWithAvatar']);
4935
+        }
4936
+        if (isset($shareParams['attributes'])) {
4937
+            $shareAttributes = $this->createMock(IShareAttributes::class);
4938
+            $shareAttributes->method('toArray')->willReturn($shareParams['attributes']);
4939
+            $shareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
4940
+            $share->setAttributes($shareAttributes);
4941
+
4942
+            $expects['attributes'] = \json_encode($shareParams['attributes']);
4943
+        }
4944
+        if (isset($shareParams['node'])) {
4945
+            $node = $this->createMock($shareParams['node']['class']);
4946
+
4947
+            $node->method('getMimeType')->willReturn($shareParams['node']['mimeType']);
4948
+
4949
+            $mountPoint = $this->createMock(IMountPoint::class);
4950
+            $mountPoint->method('getMountType')->willReturn('');
4951
+            $node->method('getMountPoint')->willReturn($mountPoint);
4952
+
4953
+            $node->method('getPath')->willReturn($shareParams['node']['path']);
4954
+            $node->method('getId')->willReturn($shareParams['node']['id']);
4955
+
4956
+            $parent = $this->createMock(Folder::class);
4957
+            $parent->method('getId')->willReturn(1);
4958
+            $node->method('getParent')->willReturn($parent);
4959
+
4960
+            $node->method('getSize')->willReturn(123456);
4961
+            $node->method('getMTime')->willReturn(1234567890);
4962
+
4963
+            $cache = $this->createMock(ICache::class);
4964
+            $cache->method('getNumericStorageId')->willReturn(100);
4965
+            $storage = $this->createMock(IStorage::class);
4966
+            $storage->method('getId')->willReturn('storageId');
4967
+            $storage->method('getCache')->willReturn($cache);
4968
+
4969
+            $node->method('getStorage')->willReturn($storage);
4970
+
4971
+            $share->setNode($node);
4972
+        }
4973
+        if (isset($shareParams['note'])) {
4974
+            $share->setNote($shareParams['note']);
4975
+        }
4976
+        if (isset($shareParams['expirationDate'])) {
4977
+            $share->setExpirationDate($shareParams['expirationDate']);
4978
+        }
4979
+        if (isset($shareParams['token'])) {
4980
+            $share->setToken($shareParams['token']);
4981
+        }
4982
+        if (isset($shareParams['label'])) {
4983
+            $share->setLabel($shareParams['label']);
4984
+        }
4985
+        if (isset($shareParams['password'])) {
4986
+            $share->setPassword($shareParams['password']);
4987
+        }
4988
+        if (isset($shareParams['sendPasswordByTalk'])) {
4989
+            $share->setSendPasswordByTalk($shareParams['sendPasswordByTalk']);
4990
+        }
4991
+
4992
+        $this->userManager->method('get')->willReturnMap($users);
4993
+
4994
+        $recipientGroup = $this->createMock(IGroup::class);
4995
+        $recipientGroup->method('getDisplayName')->willReturn('recipientGroupDisplayName');
4996
+        $this->groupManager->method('get')->willReturnMap([
4997
+            ['recipientGroup', $recipientGroup],
4998
+        ]);
4999
+
5000
+        $this->urlGenerator->method('linkToRouteAbsolute')
5001
+            ->with('files_sharing.sharecontroller.showShare', ['token' => 'myToken'])
5002
+            ->willReturn('myLink');
5003
+
5004
+        $this->rootFolder->method('getUserFolder')
5005
+            ->with($this->currentUser)
5006
+            ->willReturnSelf();
5007
+        $this->dateTimeZone->method('getTimezone')->willReturn(new \DateTimeZone('UTC'));
5008
+
5009
+        if (!$exception) {
5010
+            $this->rootFolder->method('getFirstNodeById')
5011
+                ->with($share->getNodeId())
5012
+                ->willReturn($share->getNode());
5013
+
5014
+            $this->rootFolder->method('getRelativePath')
5015
+                ->with($share->getNode()->getPath())
5016
+                ->willReturnArgument(0);
5017
+        }
5018
+
5019
+        $cm = $this->createMock(\OCP\Contacts\IManager::class);
5020
+        $this->overwriteService(\OCP\Contacts\IManager::class, $cm);
5021
+
5022
+        $cm->method('search')
5023
+            ->willReturnMap([
5024
+                ['[email protected]', ['CLOUD'], [
5025
+                    'limit' => 1,
5026
+                    'enumeration' => false,
5027
+                    'strict_search' => true,
5028
+                ],
5029
+                    [
5030
+                        [
5031
+                            'CLOUD' => [
5032
+                                '[email protected]',
5033
+                            ],
5034
+                            'FN' => 'foobar',
5035
+                        ],
5036
+                    ],
5037
+                ],
5038
+                ['[email protected]', ['EMAIL'], [
5039
+                    'limit' => 1,
5040
+                    'enumeration' => false,
5041
+                    'strict_search' => true,
5042
+                ],
5043
+                    [
5044
+                        [
5045
+                            'EMAIL' => [
5046
+                                '[email protected]',
5047
+                            ],
5048
+                            'FN' => 'mail display name',
5049
+                        ],
5050
+                    ],
5051
+                ],
5052
+            ]);
5053
+
5054
+        try {
5055
+            $result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5056
+            $this->assertFalse($exception);
5057
+            $this->assertEquals($expects, $result);
5058
+        } catch (NotFoundException $e) {
5059
+            $this->assertTrue($exception);
5060
+        }
5061
+    }
5062
+
5063
+    public static function dataFormatRoomShare(): array {
5064
+        $result = [];
5065
+
5066
+        $result[] = [
5067
+            [
5068
+                'id' => '42',
5069
+                'share_type' => IShare::TYPE_ROOM,
5070
+                'uid_owner' => 'initiator',
5071
+                'displayname_owner' => 'initiator',
5072
+                'permissions' => 1,
5073
+                'stime' => 946684862,
5074
+                'parent' => null,
5075
+                'expiration' => null,
5076
+                'token' => null,
5077
+                'uid_file_owner' => 'owner',
5078
+                'displayname_file_owner' => 'owner',
5079
+                'note' => 'personal note',
5080
+                'path' => 'file',
5081
+                'item_type' => 'file',
5082
+                'storage_id' => 'storageId',
5083
+                'storage' => 100,
5084
+                'item_source' => 3,
5085
+                'file_source' => 3,
5086
+                'file_parent' => 1,
5087
+                'file_target' => 'myTarget',
5088
+                'share_with' => 'recipientRoom',
5089
+                'share_with_displayname' => '',
5090
+                'mail_send' => 0,
5091
+                'mimetype' => 'myMimeType',
5092
+                'has_preview' => false,
5093
+                'hide_download' => 0,
5094
+                'label' => '',
5095
+                'can_edit' => false,
5096
+                'can_delete' => false,
5097
+                'item_size' => 123456,
5098
+                'item_mtime' => 1234567890,
5099
+                'is-mount-root' => false,
5100
+                'mount-type' => '',
5101
+                'attributes' => null,
5102
+                'item_permissions' => 1,
5103
+            ], false, []
5104
+        ];
5105
+
5106
+        $result[] = [
5107
+            [
5108
+                'id' => '42',
5109
+                'share_type' => IShare::TYPE_ROOM,
5110
+                'uid_owner' => 'initiator',
5111
+                'displayname_owner' => 'initiator',
5112
+                'permissions' => 1,
5113
+                'stime' => 946684862,
5114
+                'parent' => null,
5115
+                'expiration' => null,
5116
+                'token' => null,
5117
+                'uid_file_owner' => 'owner',
5118
+                'displayname_file_owner' => 'owner',
5119
+                'note' => 'personal note',
5120
+                'path' => 'file',
5121
+                'item_type' => 'file',
5122
+                'storage_id' => 'storageId',
5123
+                'storage' => 100,
5124
+                'item_source' => 3,
5125
+                'file_source' => 3,
5126
+                'file_parent' => 1,
5127
+                'file_target' => 'myTarget',
5128
+                'share_with' => 'recipientRoom',
5129
+                'share_with_displayname' => 'recipientRoomName',
5130
+                'mail_send' => 0,
5131
+                'mimetype' => 'myMimeType',
5132
+                'has_preview' => false,
5133
+                'hide_download' => 0,
5134
+                'label' => '',
5135
+                'can_edit' => false,
5136
+                'can_delete' => false,
5137
+                'item_size' => 123456,
5138
+                'item_mtime' => 1234567890,
5139
+                'is-mount-root' => false,
5140
+                'mount-type' => '',
5141
+                'attributes' => null,
5142
+                'item_permissions' => 9,
5143
+            ], true, [
5144
+                'share_with_displayname' => 'recipientRoomName'
5145
+            ]
5146
+        ];
5147
+
5148
+        return $result;
5149
+    }
5150
+
5151
+    /**
5152
+     *
5153
+     * @param array $expects
5154
+     * @param IShare $share
5155
+     * @param bool $helperAvailable
5156
+     * @param array $formatShareByHelper
5157
+     */
5158
+    #[DataProvider('dataFormatRoomShare')]
5159
+    public function testFormatRoomShare(array $expects, bool $helperAvailable, array $formatShareByHelper): void {
5160
+        $file = $this->createMock(File::class);
5161
+
5162
+        $file->method('getMimeType')->willReturn('myMimeType');
5163
+        $file->method('getPath')->willReturn('file');
5164
+        $file->method('getId')->willReturn(3);
5165
+
5166
+        $parent = $this->createMock(Folder::class);
5167
+        $parent->method('getId')->willReturn(1);
5168
+        $file->method('getParent')->willReturn($parent);
5169
+
5170
+        $file->method('getSize')->willReturn(123456);
5171
+        $file->method('getMTime')->willReturn(1234567890);
5172
+
5173
+        $mountPoint = $this->createMock(IMountPoint::class);
5174
+        $mountPoint->method('getMountType')->willReturn('');
5175
+        $file->method('getMountPoint')->willReturn($mountPoint);
5176
+
5177
+        $cache = $this->createMock(ICache::class);
5178
+        $cache->method('getNumericStorageId')->willReturn(100);
5179
+        $storage = $this->createMock(IStorage::class);
5180
+        $storage->method('getId')->willReturn('storageId');
5181
+        $storage->method('getCache')->willReturn($cache);
5182
+
5183
+        $file->method('getStorage')->willReturn($storage);
5184
+
5185
+        $share = Server::get(IManager::class)->newShare();
5186
+        $share->setShareType(IShare::TYPE_ROOM)
5187
+            ->setSharedWith('recipientRoom')
5188
+            ->setSharedBy('initiator')
5189
+            ->setShareOwner('owner')
5190
+            ->setPermissions(Constants::PERMISSION_READ)
5191
+            ->setNode($file)
5192
+            ->setShareTime(new \DateTime('2000-01-01T00:01:02'))
5193
+            ->setTarget('myTarget')
5194
+            ->setNote('personal note')
5195
+            ->setId(42);
5196
+
5197
+        $this->rootFolder->method('getUserFolder')
5198
+            ->with($this->currentUser)
5199
+            ->willReturnSelf();
5200
+
5201
+        $this->rootFolder->method('getFirstNodeById')
5202
+            ->with($share->getNodeId())
5203
+            ->willReturn($share->getNode());
5204
+
5205
+        $this->rootFolder->method('getRelativePath')
5206
+            ->with($share->getNode()->getPath())
5207
+            ->willReturnArgument(0);
5208
+
5209
+        if (!$helperAvailable) {
5210
+            $this->appManager->method('isEnabledForUser')
5211
+                ->with('spreed')
5212
+                ->willReturn(false);
5213
+        } else {
5214
+            $this->appManager->method('isEnabledForUser')
5215
+                ->with('spreed')
5216
+                ->willReturn(true);
5217
+
5218
+            // This is not possible anymore with PHPUnit 10+
5219
+            // as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
5220
+            // $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
5221
+            $helper = $this->getMockBuilder(\stdClass::class)
5222
+                ->addMethods(['formatShare', 'canAccessShare'])
5223
+                ->getMock();
5224
+            $helper->method('formatShare')
5225
+                ->with($share)
5226
+                ->willReturn($formatShareByHelper);
5227
+            $helper->method('canAccessShare')
5228
+                ->with($share)
5229
+                ->willReturn(true);
5230
+
5231
+            $this->serverContainer->method('get')
5232
+                ->with('\OCA\Talk\Share\Helper\ShareAPIController')
5233
+                ->willReturn($helper);
5234
+        }
5235
+
5236
+        $result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5237
+        $this->assertEquals($expects, $result);
5238
+    }
5239
+
5240
+    /**
5241
+     * @return list{Folder, Folder}
5242
+     */
5243
+    private function getNonSharedUserFolder(): array {
5244
+        $node = $this->getMockBuilder(Folder::class)->getMock();
5245
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
5246
+        $storage = $this->createMock(IStorage::class);
5247
+        $storage->method('instanceOfStorage')
5248
+            ->willReturnMap([
5249
+                ['OCA\Files_Sharing\External\Storage', false],
5250
+                ['OCA\Files_Sharing\SharedStorage', false],
5251
+            ]);
5252
+        $userFolder->method('getStorage')->willReturn($storage);
5253
+        $node->method('getStorage')->willReturn($storage);
5254
+        $node->method('getId')->willReturn(42);
5255
+        $user = $this->createMock(IUser::class);
5256
+        $user->method('getUID')->willReturn($this->currentUser);
5257
+        $node->method('getOwner')->willReturn($user);
5258
+        return [$userFolder, $node];
5259
+    }
5260
+
5261
+    /**
5262
+     * @return list{Folder, File}
5263
+     */
5264
+    private function getNonSharedUserFile(): array {
5265
+        $node = $this->getMockBuilder(File::class)->getMock();
5266
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
5267
+        $storage = $this->createMock(IStorage::class);
5268
+        $storage->method('instanceOfStorage')
5269
+            ->willReturnMap([
5270
+                ['OCA\Files_Sharing\External\Storage', false],
5271
+                ['OCA\Files_Sharing\SharedStorage', false],
5272
+            ]);
5273
+        $userFolder->method('getStorage')->willReturn($storage);
5274
+        $node->method('getStorage')->willReturn($storage);
5275
+        $node->method('getId')->willReturn(42);
5276
+        return [$userFolder, $node];
5277
+    }
5278
+
5279
+    public function testPopulateTags(): void {
5280
+        $tagger = $this->createMock(ITags::class);
5281
+        $this->tagManager->method('load')
5282
+            ->with('files')
5283
+            ->willReturn($tagger);
5284
+        $data = [
5285
+            ['file_source' => 10],
5286
+            ['file_source' => 22, 'foo' => 'bar'],
5287
+            ['file_source' => 42, 'x' => 'y'],
5288
+        ];
5289
+        $tags = [
5290
+            10 => ['tag3'],
5291
+            42 => ['tag1', 'tag2'],
5292
+        ];
5293
+        $tagger->method('getTagsForObjects')
5294
+            ->with([10, 22, 42])
5295
+            ->willReturn($tags);
5296
+
5297
+        $result = self::invokePrivate($this->ocs, 'populateTags', [$data]);
5298
+        $this->assertSame([
5299
+            ['file_source' => 10, 'tags' => ['tag3']],
5300
+            ['file_source' => 22, 'foo' => 'bar', 'tags' => []],
5301
+            ['file_source' => 42, 'x' => 'y', 'tags' => ['tag1', 'tag2']],
5302
+        ], $result);
5303
+    }
5304
+
5305
+    public static function trustedServerProvider(): array {
5306
+        return [
5307
+            'Trusted server' => [true, true],
5308
+            'Untrusted server' => [false, false],
5309
+        ];
5310
+    }
5311
+
5312
+    #[DataProvider('trustedServerProvider')]
5313
+    public function testFormatShareWithFederatedShare(bool $isKnownServer, bool $isTrusted): void {
5314
+        $nodeId = 12;
5315
+        $nodePath = '/test.txt';
5316
+
5317
+        $node = $this->createMock(File::class);
5318
+        $node->method('getId')->willReturn($nodeId);
5319
+        $node->method('getPath')->willReturn($nodePath);
5320
+        $node->method('getInternalPath')->willReturn(ltrim($nodePath, '/'));
5321
+        $mountPoint = $this->createMock(IMountPoint::class);
5322
+        $mountPoint->method('getMountType')->willReturn('local');
5323
+        $node->method('getMountPoint')->willReturn($mountPoint);
5324
+        $node->method('getMimetype')->willReturn('text/plain');
5325
+        $storage = $this->createMock(IStorage::class);
5326
+        $storageCache = $this->createMock(ICache::class);
5327
+        $storageCache->method('getNumericStorageId')->willReturn(1);
5328
+        $storage->method('getCache')->willReturn($storageCache);
5329
+        $storage->method('getId')->willReturn('home::shareOwner');
5330
+        $node->method('getStorage')->willReturn($storage);
5331
+        $parent = $this->createMock(Folder::class);
5332
+        $parent->method('getId')->willReturn(2);
5333
+        $node->method('getParent')->willReturn($parent);
5334
+        $node->method('getSize')->willReturn(1234);
5335
+        $node->method('getMTime')->willReturn(1234567890);
5336
+
5337
+        $share = $this->createShare(
5338
+            1,
5339
+            IShare::TYPE_REMOTE,
5340
+            '[email protected]', // shared with
5341
+            '[email protected]',      // shared by
5342
+            'shareOwner',                 // share owner
5343
+            $node,
5344
+            Constants::PERMISSION_READ,
5345
+            time(),
5346
+            null,
5347
+            2,
5348
+            $nodePath,
5349
+            $nodeId
5350
+        );
5351
+
5352
+        $this->previewManager->method('isAvailable')->with($node)->willReturn(false);
5353
+
5354
+        $this->rootFolder->method('getUserFolder')
5355
+            ->with($this->currentUser)
5356
+            ->willReturnSelf();
5357
+
5358
+        $this->rootFolder->method('getFirstNodeById')
5359
+            ->with($share->getNodeId())
5360
+            ->willReturn($node);
5361
+
5362
+        $this->rootFolder->method('getRelativePath')
5363
+            ->with($node->getPath())
5364
+            ->willReturnArgument(0);
5365
+
5366
+        $serverName = 'remoteserver.com';
5367
+        $this->trustedServers->method('isTrustedServer')
5368
+            ->with($serverName)
5369
+            ->willReturn($isKnownServer);
5370
+
5371
+        $result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5372
+
5373
+        $this->assertSame($isTrusted, $result['is_trusted_server']);
5374
+    }
5375
+
5376
+    public function testFormatShareWithFederatedShareWithAtInUsername(): void {
5377
+        $nodeId = 12;
5378
+        $nodePath = '/test.txt';
5379
+
5380
+        $node = $this->createMock(File::class);
5381
+        $node->method('getId')->willReturn($nodeId);
5382
+        $node->method('getPath')->willReturn($nodePath);
5383
+        $node->method('getInternalPath')->willReturn(ltrim($nodePath, '/'));
5384
+        $mountPoint = $this->createMock(IMountPoint::class);
5385
+        $mountPoint->method('getMountType')->willReturn('local');
5386
+        $node->method('getMountPoint')->willReturn($mountPoint);
5387
+        $node->method('getMimetype')->willReturn('text/plain');
5388
+        $storage = $this->createMock(IStorage::class);
5389
+        $storageCache = $this->createMock(ICache::class);
5390
+        $storageCache->method('getNumericStorageId')->willReturn(1);
5391
+        $storage->method('getCache')->willReturn($storageCache);
5392
+        $storage->method('getId')->willReturn('home::shareOwner');
5393
+        $node->method('getStorage')->willReturn($storage);
5394
+        $parent = $this->createMock(Folder::class);
5395
+        $parent->method('getId')->willReturn(2);
5396
+        $node->method('getParent')->willReturn($parent);
5397
+        $node->method('getSize')->willReturn(1234);
5398
+        $node->method('getMTime')->willReturn(1234567890);
5399
+
5400
+        $share = $this->createShare(
5401
+            1,
5402
+            IShare::TYPE_REMOTE,
5403
+            '[email protected]@remoteserver.com',
5404
+            '[email protected]',
5405
+            'shareOwner',
5406
+            $node,
5407
+            Constants::PERMISSION_READ,
5408
+            time(),
5409
+            null,
5410
+            2,
5411
+            $nodePath,
5412
+            $nodeId
5413
+        );
5414
+
5415
+        $this->previewManager->method('isAvailable')->with($node)->willReturn(false);
5416
+
5417
+        $this->rootFolder->method('getUserFolder')
5418
+            ->with($this->currentUser)
5419
+            ->willReturnSelf();
5420
+
5421
+        $this->rootFolder->method('getFirstNodeById')
5422
+            ->with($share->getNodeId())
5423
+            ->willReturn($node);
5424
+
5425
+        $this->rootFolder->method('getRelativePath')
5426
+            ->with($node->getPath())
5427
+            ->willReturnArgument(0);
5428
+
5429
+        $serverName = 'remoteserver.com';
5430
+        $this->trustedServers->method('isTrustedServer')
5431
+            ->with($serverName)
5432
+            ->willReturn(true);
5433
+
5434
+        $result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5435
+
5436
+        $this->assertTrue($result['is_trusted_server']);
5437
+    }
5438
+
5439
+    public function testOwnerCanAlwaysDownload(): void {
5440
+        $ocs = $this->mockFormatShare();
5441
+
5442
+        $share = $this->createMock(IShare::class);
5443
+        $node = $this->createMock(File::class);
5444
+        $userFolder = $this->createMock(Folder::class);
5445
+        $owner = $this->createMock(IUser::class);
5446
+
5447
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5448
+        $share->method('getNodeId')->willReturn(42);
5449
+        $node->method('getOwner')->willReturn($owner);
5450
+        $owner->method('getUID')->willReturn('sharedByUser');
5451
+
5452
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5453
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5454
+
5455
+        // Expect hideDownload to be set to false since owner can always download
5456
+        $share->expects($this->once())->method('setHideDownload')->with(false);
5457
+
5458
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5459
+    }
5460
+
5461
+    public function testParentHideDownloadEnforcedOnChild(): void {
5462
+        $ocs = $this->mockFormatShare();
5463
+
5464
+        $share = $this->createMock(IShare::class);
5465
+        $node = $this->createMock(File::class);
5466
+        $userFolder = $this->createMock(Folder::class);
5467
+        $owner = $this->createMock(IUser::class);
5468
+        $storage = $this->createMock(SharedStorage::class);
5469
+        $originalShare = $this->createMock(IShare::class);
5470
+
5471
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5472
+        $share->method('getNodeId')->willReturn(42);
5473
+        $share->method('getHideDownload')->willReturn(false); // User wants to allow downloads
5474
+        $node->method('getOwner')->willReturn($owner);
5475
+        $owner->method('getUID')->willReturn('differentOwner');
5476
+        $node->method('getStorage')->willReturn($storage);
5477
+        $storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5478
+        $storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5479
+        $storage->method('getShare')->willReturn($originalShare);
5480
+        $originalShare->method('getHideDownload')->willReturn(true); // Parent hides download
5481
+        $originalShare->method('getAttributes')->willReturn(null);
5482
+
5483
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5484
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5485
+
5486
+        // Should be forced to hide download due to parent restriction
5487
+        $share->expects($this->once())->method('setHideDownload')->with(true);
5488
+
5489
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5490
+    }
5491
+
5492
+    public function testUserCanHideWhenParentAllows(): void {
5493
+        $ocs = $this->mockFormatShare();
5494
+
5495
+        $share = $this->createMock(IShare::class);
5496
+        $node = $this->createMock(File::class);
5497
+        $userFolder = $this->createMock(Folder::class);
5498
+        $owner = $this->createMock(IUser::class);
5499
+        $storage = $this->createMock(SharedStorage::class);
5500
+        $originalShare = $this->createMock(IShare::class);
5501
+
5502
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5503
+        $share->method('getNodeId')->willReturn(42);
5504
+        $share->method('getHideDownload')->willReturn(true); // User chooses to hide downloads
5505
+        $node->method('getOwner')->willReturn($owner);
5506
+        $owner->method('getUID')->willReturn('differentOwner');
5507
+        $node->method('getStorage')->willReturn($storage);
5508
+        $storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5509
+        $storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5510
+        $storage->method('getShare')->willReturn($originalShare);
5511
+        $originalShare->method('getHideDownload')->willReturn(false); // Parent allows download
5512
+        $originalShare->method('getAttributes')->willReturn(null);
5513
+
5514
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5515
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5516
+
5517
+        // Should respect user's choice to hide downloads
5518
+        $share->expects($this->once())->method('setHideDownload')->with(true);
5519
+
5520
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5521
+    }
5522
+
5523
+    public function testParentDownloadAttributeInherited(): void {
5524
+        $ocs = $this->mockFormatShare();
5525
+
5526
+        $share = $this->createMock(IShare::class);
5527
+        $node = $this->createMock(File::class);
5528
+        $userFolder = $this->createMock(Folder::class);
5529
+        $owner = $this->createMock(IUser::class);
5530
+        $storage = $this->createMock(SharedStorage::class);
5531
+        $originalShare = $this->createMock(IShare::class);
5532
+        $attributes = $this->createMock(IShareAttributes::class);
5533
+        $shareAttributes = $this->createMock(IShareAttributes::class);
5534
+
5535
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5536
+        $share->method('getNodeId')->willReturn(42);
5537
+        $share->method('getHideDownload')->willReturn(false); // User wants to allow downloads
5538
+        $share->method('getAttributes')->willReturn($shareAttributes);
5539
+        $share->method('newAttributes')->willReturn($shareAttributes);
5540
+        $node->method('getOwner')->willReturn($owner);
5541
+        $owner->method('getUID')->willReturn('differentOwner');
5542
+        $node->method('getStorage')->willReturn($storage);
5543
+        $storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5544
+        $storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5545
+        $storage->method('getShare')->willReturn($originalShare);
5546
+        $originalShare->method('getHideDownload')->willReturn(false);
5547
+        $originalShare->method('getAttributes')->willReturn($attributes);
5548
+        $attributes->method('getAttribute')->with('permissions', 'download')->willReturn(false); // Parent forbids download
5549
+
5550
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5551
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5552
+
5553
+        // Should be forced to hide download and set download attribute to false
5554
+        $share->expects($this->once())->method('setHideDownload')->with(true);
5555
+        $shareAttributes->expects($this->once())->method('setAttribute')->with('permissions', 'download', false);
5556
+        $share->expects($this->once())->method('setAttributes')->with($shareAttributes);
5557
+
5558
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5559
+    }
5560
+
5561
+    public function testFederatedStorageRespectsUserChoice(): void {
5562
+        $ocs = $this->mockFormatShare();
5563
+
5564
+        $share = $this->createMock(IShare::class);
5565
+        $node = $this->createMock(File::class);
5566
+        $userFolder = $this->createMock(Folder::class);
5567
+        $owner = $this->createMock(IUser::class);
5568
+        $storage = $this->createMock(Storage::class);
5569
+
5570
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5571
+        $share->method('getNodeId')->willReturn(42);
5572
+        $share->method('getHideDownload')->willReturn(true); // User chooses to hide downloads
5573
+        $node->method('getOwner')->willReturn($owner);
5574
+        $owner->method('getUID')->willReturn('differentOwner');
5575
+        $node->method('getStorage')->willReturn($storage);
5576
+        $storage->method('instanceOfStorage')->willReturnMap([
5577
+            [SharedStorage::class, false],
5578
+            [Storage::class, true]
5579
+        ]);
5580
+
5581
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5582
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5583
+
5584
+        // For federated storage, should respect user's choice
5585
+        $share->expects($this->once())->method('setHideDownload')->with(true);
5586
+
5587
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5588
+    }
5589
+
5590
+    public function testUserAllowsDownloadWhenParentPermits(): void {
5591
+        $ocs = $this->mockFormatShare();
5592
+
5593
+        $share = $this->createMock(IShare::class);
5594
+        $node = $this->createMock(File::class);
5595
+        $userFolder = $this->createMock(Folder::class);
5596
+        $owner = $this->createMock(IUser::class);
5597
+        $storage = $this->createMock(SharedStorage::class);
5598
+        $originalShare = $this->createMock(IShare::class);
5599
+
5600
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5601
+        $share->method('getNodeId')->willReturn(42);
5602
+        $share->method('getHideDownload')->willReturn(false); // User wants to allow downloads
5603
+        $node->method('getOwner')->willReturn($owner);
5604
+        $owner->method('getUID')->willReturn('differentOwner');
5605
+        $node->method('getStorage')->willReturn($storage);
5606
+        $storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5607
+        $storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5608
+        $storage->method('getShare')->willReturn($originalShare);
5609
+        $originalShare->method('getHideDownload')->willReturn(false); // Parent allows download
5610
+        $originalShare->method('getAttributes')->willReturn(null);
5611
+
5612
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5613
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5614
+
5615
+        // Should allow downloads as both user and parent permit it
5616
+        $share->expects($this->once())->method('setHideDownload')->with(false);
5617
+
5618
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5619
+    }
5620
+
5621
+    public function testWrapperStorageUnwrapped(): void {
5622
+        $ocs = $this->mockFormatShare();
5623
+
5624
+        $share = $this->createMock(IShare::class);
5625
+        $node = $this->createMock(File::class);
5626
+        $userFolder = $this->createMock(Folder::class);
5627
+        $owner = $this->createMock(IUser::class);
5628
+        $wrapperStorage = $this->createMock(Wrapper::class);
5629
+        $innerStorage = $this->createMock(SharedStorage::class);
5630
+        $originalShare = $this->createMock(IShare::class);
5631
+
5632
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5633
+        $share->method('getNodeId')->willReturn(42);
5634
+        $share->method('getHideDownload')->willReturn(false);
5635
+        $node->method('getOwner')->willReturn($owner);
5636
+        $owner->method('getUID')->willReturn('differentOwner');
5637
+        $node->method('getStorage')->willReturn($wrapperStorage);
5638
+        $wrapperStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5639
+        $wrapperStorage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($innerStorage);
5640
+        $innerStorage->method('getShare')->willReturn($originalShare);
5641
+        $originalShare->method('getHideDownload')->willReturn(false);
5642
+        $originalShare->method('getAttributes')->willReturn(null);
5643
+
5644
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5645
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5646
+
5647
+        $share->expects($this->once())->method('setHideDownload')->with(false);
5648
+
5649
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5650
+    }
5651 5651
 }
Please login to merge, or discard this patch.