FileUtils.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Core\Command\Info;
  8. use OCA\Circles\MountManager\CircleMount;
  9. use OCA\Files_External\Config\ExternalMountPoint;
  10. use OCA\Files_Sharing\SharedMount;
  11. use OCA\GroupFolders\Mount\GroupMountPoint;
  12. use OCP\Constants;
  13. use OCP\Files\Config\IUserMountCache;
  14. use OCP\Files\FileInfo;
  15. use OCP\Files\Folder;
  16. use OCP\Files\IHomeStorage;
  17. use OCP\Files\IRootFolder;
  18. use OCP\Files\Mount\IMountPoint;
  19. use OCP\Files\Node;
  20. use OCP\Files\NotFoundException;
  21. use OCP\Share\IShare;
  22. use OCP\Util;
  23. use Symfony\Component\Console\Output\OutputInterface;
  24. class FileUtils {
  25. public function __construct(
  26. private IRootFolder $rootFolder,
  27. private IUserMountCache $userMountCache,
  28. ) {
  29. }
  30. /**
  31. * @param FileInfo $file
  32. * @return array<string, Node[]>
  33. * @throws \OCP\Files\NotPermittedException
  34. * @throws \OC\User\NoUserException
  35. */
  36. public function getFilesByUser(FileInfo $file): array {
  37. $id = $file->getId();
  38. if (!$id) {
  39. return [];
  40. }
  41. $mounts = $this->userMountCache->getMountsForFileId($id);
  42. $result = [];
  43. foreach ($mounts as $mount) {
  44. if (isset($result[$mount->getUser()->getUID()])) {
  45. continue;
  46. }
  47. $userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID());
  48. $result[$mount->getUser()->getUID()] = $userFolder->getById($id);
  49. }
  50. return $result;
  51. }
  52. /**
  53. * Get file by either id of path
  54. *
  55. * @param string $fileInput
  56. * @return Node|null
  57. */
  58. public function getNode(string $fileInput): ?Node {
  59. if (is_numeric($fileInput)) {
  60. $mounts = $this->userMountCache->getMountsForFileId((int)$fileInput);
  61. if (!$mounts) {
  62. return null;
  63. }
  64. $mount = reset($mounts);
  65. $userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID());
  66. return $userFolder->getFirstNodeById((int)$fileInput);
  67. } else {
  68. try {
  69. return $this->rootFolder->get($fileInput);
  70. } catch (NotFoundException $e) {
  71. return null;
  72. }
  73. }
  74. }
  75. public function formatPermissions(string $type, int $permissions): string {
  76. if ($permissions == Constants::PERMISSION_ALL || ($type === 'file' && $permissions == (Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE))) {
  77. return 'full permissions';
  78. }
  79. $perms = [];
  80. $allPerms = [Constants::PERMISSION_READ => 'read', Constants::PERMISSION_UPDATE => 'update', Constants::PERMISSION_CREATE => 'create', Constants::PERMISSION_DELETE => 'delete', Constants::PERMISSION_SHARE => 'share'];
  81. foreach ($allPerms as $perm => $name) {
  82. if (($permissions & $perm) === $perm) {
  83. $perms[] = $name;
  84. }
  85. }
  86. return implode(', ', $perms);
  87. }
  88. /**
  89. * @psalm-suppress UndefinedClass
  90. * @psalm-suppress UndefinedInterfaceMethod
  91. */
  92. public function formatMountType(IMountPoint $mountPoint): string {
  93. $storage = $mountPoint->getStorage();
  94. if ($storage && $storage->instanceOfStorage(IHomeStorage::class)) {
  95. return 'home storage';
  96. } elseif ($mountPoint instanceof SharedMount) {
  97. $share = $mountPoint->getShare();
  98. $shares = $mountPoint->getGroupedShares();
  99. $sharedBy = array_map(function (IShare $share) {
  100. $shareType = $this->formatShareType($share);
  101. if ($shareType) {
  102. return $share->getSharedBy() . ' (via ' . $shareType . ' ' . $share->getSharedWith() . ')';
  103. } else {
  104. return $share->getSharedBy();
  105. }
  106. }, $shares);
  107. $description = 'shared by ' . implode(', ', $sharedBy);
  108. if ($share->getSharedBy() !== $share->getShareOwner()) {
  109. $description .= ' owned by ' . $share->getShareOwner();
  110. }
  111. return $description;
  112. } elseif ($mountPoint instanceof GroupMountPoint) {
  113. return 'groupfolder ' . $mountPoint->getFolderId();
  114. } elseif ($mountPoint instanceof ExternalMountPoint) {
  115. return 'external storage ' . $mountPoint->getStorageConfig()->getId();
  116. } elseif ($mountPoint instanceof CircleMount) {
  117. return 'circle';
  118. }
  119. return get_class($mountPoint);
  120. }
  121. public function formatShareType(IShare $share): ?string {
  122. switch ($share->getShareType()) {
  123. case IShare::TYPE_GROUP:
  124. return 'group';
  125. case IShare::TYPE_CIRCLE:
  126. return 'circle';
  127. case IShare::TYPE_DECK:
  128. return 'deck';
  129. case IShare::TYPE_ROOM:
  130. return 'room';
  131. case IShare::TYPE_USER:
  132. return null;
  133. default:
  134. return 'Unknown (' . $share->getShareType() . ')';
  135. }
  136. }
  137. /**
  138. * Print out the largest count($sizeLimits) files in the directory tree
  139. *
  140. * @param OutputInterface $output
  141. * @param Folder $node
  142. * @param string $prefix
  143. * @param array $sizeLimits largest items that are still in the queue to be printed, ordered ascending
  144. * @return int how many items we've printed
  145. */
  146. public function outputLargeFilesTree(
  147. OutputInterface $output,
  148. Folder $node,
  149. string $prefix,
  150. array &$sizeLimits,
  151. bool $all,
  152. ): int {
  153. /**
  154. * Algorithm to print the N largest items in a folder without requiring to query or sort the entire three
  155. *
  156. * This is done by keeping a list ($sizeLimits) of size N that contain the largest items outside of this
  157. * folders that are could be printed if there aren't enough items in this folder that are larger.
  158. *
  159. * We loop over the items in this folder by size descending until the size of the item falls before the smallest
  160. * size in $sizeLimits (at that point there are enough items outside this folder to complete the N items).
  161. *
  162. * When encountering a folder, we create an updated $sizeLimits with the largest items in the current folder still
  163. * remaining which we pass into the recursion. (We don't update the current $sizeLimits because that should only
  164. * hold items *outside* of the current folder.)
  165. *
  166. * For every item printed we remove the first item of $sizeLimits are there is no longer room in the output to print
  167. * items that small.
  168. */
  169. $count = 0;
  170. $children = $node->getDirectoryListing();
  171. usort($children, function (Node $a, Node $b) {
  172. return $b->getSize() <=> $a->getSize();
  173. });
  174. foreach ($children as $i => $child) {
  175. if (!$all) {
  176. if (count($sizeLimits) === 0 || $child->getSize() < $sizeLimits[0]) {
  177. return $count;
  178. }
  179. array_shift($sizeLimits);
  180. }
  181. $count += 1;
  182. /** @var Node $child */
  183. $output->writeln("$prefix- " . $child->getName() . ': <info>' . Util::humanFileSize($child->getSize()) . '</info>');
  184. if ($child instanceof Folder) {
  185. $recurseSizeLimits = $sizeLimits;
  186. if (!$all) {
  187. for ($j = 0; $j < count($recurseSizeLimits); $j++) {
  188. if (isset($children[$i + $j + 1])) {
  189. $nextChildSize = $children[$i + $j + 1]->getSize();
  190. if ($nextChildSize > $recurseSizeLimits[0]) {
  191. array_shift($recurseSizeLimits);
  192. $recurseSizeLimits[] = $nextChildSize;
  193. }
  194. }
  195. }
  196. sort($recurseSizeLimits);
  197. }
  198. $recurseCount = $this->outputLargeFilesTree($output, $child, $prefix . ' ', $recurseSizeLimits, $all);
  199. $sizeLimits = array_slice($sizeLimits, $recurseCount);
  200. $count += $recurseCount;
  201. }
  202. }
  203. return $count;
  204. }
  205. }