Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/files list occ command #43342

Closed
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
027d692
files:list added as a occ command. filter added based on type and siz…
yemkareems Feb 3, 2024
b9caee2
basic sorting added to list, could be improvised
yemkareems Feb 4, 2024
f90b106
listing files and folders separately as sorting is not working as exp…
yemkareems Feb 4, 2024
1503053
general refactoring to remove unused variables and functions, edge ca…
yemkareems Feb 4, 2024
2b915f3
Update ListFiles.php
yemkareems Feb 7, 2024
7e27fbe
composer run cs:fix
yemkareems Feb 7, 2024
d82b3b3
Update ListFiles.php
yemkareems Feb 7, 2024
a035d41
type hinting
yemkareems Feb 7, 2024
ac4efa8
PR review comments by Louis done
yemkareems Feb 9, 2024
92e5516
path made a argument and user extraction logic based on path changed
yemkareems Feb 9, 2024
d59769e
help doc content changed to start with all first caps
yemkareems Feb 9, 2024
1764794
removed the user loop since only one user is there and copy right aut…
yemkareems Feb 12, 2024
5638ef2
type hinting corrected
yemkareems Feb 12, 2024
79b5410
cs fix ran
yemkareems Feb 13, 2024
4058838
array multisort assigning to var and passing it
yemkareems Feb 13, 2024
90f5d26
userFolder type hint to fix psalm error
yemkareems Feb 13, 2024
351d671
Update apps/files/lib/Command/ListFiles.php
yemkareems Feb 13, 2024
0cfb4dd
updated the description of path
yemkareems Feb 16, 2024
fc20909
making changes to list folders as directory type
yemkareems Feb 18, 2024
14078fc
cs fix run for the file
yemkareems Feb 18, 2024
9b129a6
telling psalm correct path of folder
yemkareems Apr 4, 2024
57752e6
feat: tell psalm the correct type associated with userFolder
yemkareems Apr 4, 2024
e8573aa
feat: tell psalm the correct type associated with userFolder
yemkareems Apr 4, 2024
c5ded86
feat: license wording changed as suggested
yemkareems Apr 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/files/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

<commands>
<command>OCA\Files\Command\Scan</command>
<command>OCA\Files\Command\ListFiles</command>
<command>OCA\Files\Command\DeleteOrphanedFiles</command>
<command>OCA\Files\Command\TransferOwnership</command>
<command>OCA\Files\Command\ScanAppData</command>
Expand Down
3 changes: 2 additions & 1 deletion apps/files/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
'OCA\\Files\\Command\\Object\\Put' => $baseDir . '/../lib/Command/Object/Put.php',
'OCA\\Files\\Command\\Put' => $baseDir . '/../lib/Command/Put.php',
'OCA\\Files\\Command\\RepairTree' => $baseDir . '/../lib/Command/RepairTree.php',
'OCA\\Files\\Command\\Scan' => $baseDir . '/../lib/Command/Scan.php',
'OCA\\Files\\Command\\Scan' => $baseDir . '/../lib/Command/Scan.php',
'OCA\\Files\\Command\\ListFiles' => $baseDir . '/../lib/Command/ListFiles.php',
'OCA\\Files\\Command\\ScanAppData' => $baseDir . '/../lib/Command/ScanAppData.php',
'OCA\\Files\\Command\\TransferOwnership' => $baseDir . '/../lib/Command/TransferOwnership.php',
'OCA\\Files\\Controller\\ApiController' => $baseDir . '/../lib/Controller/ApiController.php',
Expand Down
7 changes: 4 additions & 3 deletions apps/files/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
class ComposerStaticInitFiles
{
public static $prefixLengthsPsr4 = array (
'O' =>
'O' =>
array (
'OCA\\Files\\' => 10,
),
);

public static $prefixDirsPsr4 = array (
'OCA\\Files\\' =>
'OCA\\Files\\' =>
array (
0 => __DIR__ . '/..' . '/../lib',
),
Expand Down Expand Up @@ -53,7 +53,8 @@ class ComposerStaticInitFiles
'OCA\\Files\\Command\\Object\\Put' => __DIR__ . '/..' . '/../lib/Command/Object/Put.php',
'OCA\\Files\\Command\\Put' => __DIR__ . '/..' . '/../lib/Command/Put.php',
'OCA\\Files\\Command\\RepairTree' => __DIR__ . '/..' . '/../lib/Command/RepairTree.php',
'OCA\\Files\\Command\\Scan' => __DIR__ . '/..' . '/../lib/Command/Scan.php',
'OCA\\Files\\Command\\Scan' => __DIR__ . '/..' . '/../lib/Command/Scan.php',
'OCA\\Files\\Command\\ListFiles' => __DIR__ . '/..' . '/../lib/Command/ListFiles.php',
'OCA\\Files\\Command\\ScanAppData' => __DIR__ . '/..' . '/../lib/Command/ScanAppData.php',
'OCA\\Files\\Command\\TransferOwnership' => __DIR__ . '/..' . '/../lib/Command/TransferOwnership.php',
'OCA\\Files\\Controller\\ApiController' => __DIR__ . '/..' . '/../lib/Controller/ApiController.php',
Expand Down
334 changes: 334 additions & 0 deletions apps/files/lib/Command/ListFiles.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Bart Visscher <[email protected]>
* @author Blaok <[email protected]>
* @author Christoph Wurst <[email protected]>
* @author Daniel Kesselberg <[email protected]>
* @author J0WI <[email protected]>
* @author Joas Schilling <[email protected]>
* @author Joel S <[email protected]>
* @author Jörn Friedrich Dreyer <[email protected]>
* @author [email protected] <[email protected]>
* @author Maxence Lange <[email protected]>
* @author Robin Appelman <[email protected]>
* @author Roeland Jago Douma <[email protected]>
* @author Thomas Müller <[email protected]>
* @author Vincent Petry <[email protected]>
artonge marked this conversation as resolved.
Show resolved Hide resolved
* @author Kareem <[email protected]>
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
yemkareems marked this conversation as resolved.
Show resolved Hide resolved
namespace OCA\Files\Command;

use OC\Core\Command\Base;
use OC\Core\Command\InterruptedException;
use OC\Files\Node\Node;
use OC\FilesMetadata\FilesMetadataManager;
use OC\ForbiddenException;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\IUserManager;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class ListFiles extends Base {
protected array $fileInfo = [];
protected array $dirInfo = [];
public function __construct(
private IUserManager $userManager,
private IRootFolder $rootFolder,
private FilesMetadataManager $filesMetadataManager,
private IEventDispatcher $eventDispatcher,
private LoggerInterface $logger
) {
parent::__construct();
}

protected function configure(): void {
parent::configure();

$this->setName("files:list")
->setDescription("List filesystem")
->addArgument(
"path",
InputArgument::REQUIRED,
'Limit list to this path, eg. path="/alice/files/Music", the user_id is determined by the path parameter'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description no longer matches with it being moved to and argument. (the path=)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@icewind1991 Updated the description of the path argument

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean the path=... doesn't match anymore as it's no longer an option, so you no longer specify the path= part

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @icewind1991 not sure if i understood it correctly because i still do occ files:list path=/admin/files/Media --type=video where path is required and the above lists all the video files in the path

)
->addOption("type", "", InputArgument::OPTIONAL, "Filter by type like application, image, video etc")
->addOption(
"minSize",
'0',
InputArgument::OPTIONAL,
"Filter by min size"
)
->addOption(
"maxSize",
'0',
InputArgument::OPTIONAL,
"Filter by max size"
)
->addOption(
"sort",
"name",
InputArgument::OPTIONAL,
"Sort by name, path, size, owner, type, perm, created-at"
)
->addOption("order", "ASC", InputArgument::OPTIONAL, "Order is either ASC or DESC");
}

private function getNodeInfo(Node $node): array {
return [
"name" => $node->getName(),
"size" => $node->getSize() . " bytes",
"perm" => $node->getPermissions(),
"owner" => $node->getOwner()->getDisplayName(),

Check notice

Code scanning / Psalm

PossiblyNullReference Note

Cannot call method getDisplayName on possibly null value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"owner" => $node->getOwner()->getDisplayName(),
"owner" => $node->getOwner()?->getDisplayName(),

"created-at" => $node->getCreationTime(),
"type" => $node->getMimePart(),
yemkareems marked this conversation as resolved.
Show resolved Hide resolved
];
}

protected function listFiles(
string $user,
string $path,
OutputInterface $output,
?string $type = "",
?int $minSize = 0,
?int $maxSize = 0
): void {
try {
$userFolder = $this->rootFolder->get($path);
Fixed Show fixed Hide fixed
$files = $userFolder->getDirectoryListing();

Check failure on line 120 in apps/files/lib/Command/ListFiles.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedInterfaceMethod

apps/files/lib/Command/ListFiles.php:120:26: UndefinedInterfaceMethod: Method OCP\Files\Node::getDirectoryListing does not exist (see https://psalm.dev/181)
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show resolved Hide resolved
Fixed Show fixed Hide fixed
foreach ($files as $file) {
$includeType = $includeMin = $includeMax = true;
if ($type != "" && $type != $file->getMimePart()) {
$includeType = false;
}
if ($minSize > 0) {
$includeMin = $file->getSize() >= $minSize;
}
if ($maxSize > 0) {
$includeMax = $file->getSize() <= $maxSize;
}
if ($file instanceof File) {
if ($includeType && $includeMin && $includeMax) {
$this->fileInfo[] = $this->getNodeInfo($file);

Check failure on line 134 in apps/files/lib/Command/ListFiles.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidArgument

apps/files/lib/Command/ListFiles.php:134:46: InvalidArgument: Argument 1 of OCA\Files\Command\ListFiles::getNodeInfo expects OC\Files\Node\Node, but OCP\Files\File provided (see https://psalm.dev/004)
Fixed Show fixed Hide fixed
}
} elseif ($file instanceof Folder) {
if ($includeType && $includeMin && $includeMax) {
$this->dirInfo[] = $this->getNodeInfo($file);

Check failure on line 138 in apps/files/lib/Command/ListFiles.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidArgument

apps/files/lib/Command/ListFiles.php:138:45: InvalidArgument: Argument 1 of OCA\Files\Command\ListFiles::getNodeInfo expects OC\Files\Node\Node, but OCP\Files\Folder provided (see https://psalm.dev/004)
Fixed Show fixed Hide fixed
}
}
}
} catch (ForbiddenException $e) {
$output->writeln(
"<error>Home storage for user $user not writable or 'files' subdirectory missing</error>"
);
$output->writeln(" " . $e->getMessage());
$output->writeln(
'Make sure you\'re running the list command only as the user the web server runs as'
);
} catch (InterruptedException $e) {
# exit the function if ctrl-c has been pressed
$output->writeln("Interrupted by user");
} catch (NotFoundException $e) {
$output->writeln(
"<error>Path not found: " . $e->getMessage() . "</error>"
);
} catch (\Exception $e) {
$output->writeln(
"<error>Exception during list: " . $e->getMessage() . "</error>"
);
$output->writeln("<error>" . $e->getTraceAsString() . "</error>");
}
}

protected function execute(
InputInterface $input,
OutputInterface $output
): int {
$inputPath = $input->getArgument("path");

$users = [];
if ($inputPath) {
$inputPath = ltrim($inputPath, "path=");
[, $user] = explode("/", rtrim($inputPath, "/").'/', 4);
$users = [$user];
}

# check quantity of users to be process and show it on the command line
$users_total = count($users);
yemkareems marked this conversation as resolved.
Show resolved Hide resolved
if ($users_total === 0) {
$output->writeln(
"<error>Please specify the path to list, path=...</error>"
);
return self::FAILURE;
}

$this->initTools($output);

$user_count = 0;
foreach ($users as $user) {
if (is_object($user)) {
Fixed Show fixed Hide fixed
$user = $user->getUID();
}
$path = $inputPath ?: "/" . $user;
++$user_count;
if ($this->userManager->userExists($user)) {
$output->writeln(
"Starting list for user $user_count out of $users_total ($user)"
);
$this->listFiles(
$user,
$path,
$output,
$input->getOption("type"),
$input->getOption("minSize"),
$input->getOption("maxSize")
);
} else {
$output->writeln(
"<error>Unknown user $user_count $user</error>"
);
$output->writeln("", OutputInterface::VERBOSITY_VERBOSE);
}

try {
$this->abortIfInterrupted();
} catch (InterruptedException $e) {
break;
}
}

$this->presentStats($input, $output);
return self::SUCCESS;
}

/**
* Initialises some useful tools for the Command
*/
protected function initTools(OutputInterface $output): void {
// Convert PHP errors to exceptions
set_error_handler(
fn (
int $severity,
string $message,
string $file,
int $line
): bool => $this->exceptionErrorHandler(
$output,
$severity,
$message,
$file,
$line
),
E_ALL
);
}

/**
* Processes PHP errors in order to be able to show them in the output
*
* @see https://www.php.net/manual/en/function.set-error-handler.php
*
* @param int $severity the level of the error raised
* @param string $message
* @param string $file the filename that the error was raised in
* @param int $line the line number the error was raised
*/
public function exceptionErrorHandler(
OutputInterface $output,
int $severity,
string $message,
string $file,
int $line
): bool {
if ($severity === E_DEPRECATED || $severity === E_USER_DEPRECATED) {
// Do not show deprecation warnings
return false;
}
$e = new \ErrorException($message, 0, $severity, $file, $line);
$output->writeln(
"<error>Error during list: " . $e->getMessage() . "</error>"
);
$output->writeln(
"<error>" . $e->getTraceAsString() . "</error>",
OutputInterface::VERBOSITY_VERY_VERBOSE
);
return true;
}

protected function presentStats(
InputInterface $input,
OutputInterface $output
): void {
$headers = [
"Permission",
"Size",
"Owner",
"Created at",
"Filename",
"Type",
];
$rows = [];
$fileInfo = $this->fileInfo[0] ?? [];
$sortKey = array_key_exists($input->getOption("sort"), $fileInfo)
? $input->getOption("sort")
: "name";
$order = $input->getOption("order") == "ASC" ? SORT_ASC : SORT_DESC;
array_multisort(
array_column($this->fileInfo, $sortKey),

Check failure on line 299 in apps/files/lib/Command/ListFiles.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidPassByReference

apps/files/lib/Command/ListFiles.php:299:4: InvalidPassByReference: Parameter 1 of array_multisort expects a variable (see https://psalm.dev/102)
Fixed Show fixed Hide fixed
$order,
$this->fileInfo
);
array_multisort(
array_column($this->dirInfo, $sortKey),

Check failure on line 304 in apps/files/lib/Command/ListFiles.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidPassByReference

apps/files/lib/Command/ListFiles.php:304:4: InvalidPassByReference: Parameter 1 of array_multisort expects a variable (see https://psalm.dev/102)
Fixed Show fixed Hide fixed
$order,
$this->dirInfo
);

foreach ($this->fileInfo as $k => $item) {
$rows[$k] = [
$item["perm"],
$item["size"],
$item["owner"],
$item["created-at"],
$item["name"],
$item["type"],
];
}
foreach ($this->dirInfo as $k => $item) {
$rows[] = [
$item["perm"],
$item["size"],
$item["owner"],
$item["created-at"],
$item["name"],
$item["type"],
];
}

$table = new Table($output);
$table->setHeaders($headers)->setRows($rows);
$table->render();
}
}
Loading