Refactors files app commands.

To improve code readability.

Signed-off-by: Faraz Samapoor <fsa@adlas.at>
pull/39150/head
Faraz Samapoor 3 years ago committed by Faraz Samapoor
parent 706c141fff
commit 5d242aa2f8
  1. 14
      apps/files/lib/Command/Delete.php
  2. 16
      apps/files/lib/Command/DeleteOrphanedFiles.php
  3. 60
      apps/files/lib/Command/Get.php
  4. 9
      apps/files/lib/Command/Object/Delete.php
  5. 41
      apps/files/lib/Command/Object/Get.php
  6. 41
      apps/files/lib/Command/Object/ObjectUtil.php
  7. 14
      apps/files/lib/Command/Object/Put.php
  8. 21
      apps/files/lib/Command/Put.php
  9. 16
      apps/files/lib/Command/RepairTree.php
  10. 37
      apps/files/lib/Command/Scan.php
  11. 60
      apps/files/lib/Command/ScanAppData.php
  12. 38
      apps/files/lib/Command/TransferOwnership.php

@ -35,10 +35,9 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
class Delete extends Command {
private FileUtils $fileUtils;
public function __construct(FileUtils $fileUtils) {
$this->fileUtils = $fileUtils;
public function __construct(
private FileUtils $fileUtils,
) {
parent::__construct();
}
@ -58,7 +57,7 @@ class Delete extends Command {
if (!$node) {
$output->writeln("<error>file $fileInput not found</error>");
return 1;
return self::FAILURE;
}
$deleteConfirmed = $force;
@ -72,7 +71,7 @@ class Delete extends Command {
$question = new ConfirmationQuestion("<info>$fileInput</info> in a shared file, do you want to unshare the file from <info>$user</info> instead of deleting the source file? [Y/n] ", true);
if ($helper->ask($input, $output, $question)) {
$storage->unshareStorage();
return 0;
return self::SUCCESS;
} else {
$node = $storage->getShare()->getNode();
$output->writeln("");
@ -110,7 +109,6 @@ class Delete extends Command {
}
}
return 0;
return self::SUCCESS;
}
}

@ -35,17 +35,13 @@ use Symfony\Component\Console\Output\OutputInterface;
class DeleteOrphanedFiles extends Command {
public const CHUNK_SIZE = 200;
/**
* @var IDBConnection
*/
protected $connection;
public function __construct(IDBConnection $connection) {
$this->connection = $connection;
public function __construct(
protected IDBConnection $connection,
) {
parent::__construct();
}
protected function configure() {
protected function configure(): void {
$this
->setName('files:cleanup')
->setDescription('cleanup filecache');
@ -81,10 +77,10 @@ class DeleteOrphanedFiles extends Command {
$deletedMounts = $this->cleanupOrphanedMounts();
$output->writeln("$deletedMounts orphaned mount entries deleted");
return 0;
return self::SUCCESS;
}
private function cleanupOrphanedMounts() {
private function cleanupOrphanedMounts(): int {
$deletedEntries = 0;
$query = $this->connection->getQueryBuilder();

@ -32,10 +32,9 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class Get extends Command {
private FileUtils $fileUtils;
public function __construct(FileUtils $fileUtils) {
$this->fileUtils = $fileUtils;
public function __construct(
private FileUtils $fileUtils,
) {
parent::__construct();
}
@ -54,36 +53,35 @@ class Get extends Command {
if (!$node) {
$output->writeln("<error>file $fileInput not found</error>");
return 1;
return self::FAILURE;
}
if ($node instanceof File) {
$isTTY = stream_isatty(STDOUT);
if ($outputName === null && $isTTY && $node->getMimePart() !== 'text') {
$output->writeln([
"<error>Warning: Binary output can mess up your terminal</error>",
" Use <info>occ files:get $fileInput -</info> to output it to the terminal anyway",
" Or <info>occ files:get $fileInput <FILE></info> to save to a file instead"
]);
return 1;
}
$source = $node->fopen('r');
if (!$source) {
$output->writeln("<error>Failed to open $fileInput for reading</error>");
return 1;
}
$target = ($outputName === null || $outputName === '-') ? STDOUT : fopen($outputName, 'w');
if (!$target) {
$output->writeln("<error>Failed to open $outputName for reading</error>");
return 1;
}
stream_copy_to_stream($source, $target);
return 0;
} else {
if (!($node instanceof File)) {
$output->writeln("<error>$fileInput is a directory</error>");
return 1;
return self::FAILURE;
}
$isTTY = stream_isatty(STDOUT);
if ($outputName === null && $isTTY && $node->getMimePart() !== 'text') {
$output->writeln([
"<error>Warning: Binary output can mess up your terminal</error>",
" Use <info>occ files:get $fileInput -</info> to output it to the terminal anyway",
" Or <info>occ files:get $fileInput <FILE></info> to save to a file instead"
]);
return self::FAILURE;
}
$source = $node->fopen('r');
if (!$source) {
$output->writeln("<error>Failed to open $fileInput for reading</error>");
return self::FAILURE;
}
$target = ($outputName === null || $outputName === '-') ? STDOUT : fopen($outputName, 'w');
if (!$target) {
$output->writeln("<error>Failed to open $outputName for reading</error>");
return self::FAILURE;
}
}
stream_copy_to_stream($source, $target);
return self::SUCCESS;
}
}

@ -34,10 +34,9 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
class Delete extends Command {
private ObjectUtil $objectUtils;
public function __construct(ObjectUtil $objectUtils) {
$this->objectUtils = $objectUtils;
public function __construct(
private ObjectUtil $objectUtils,
) {
parent::__construct();
}
@ -73,6 +72,6 @@ class Delete extends Command {
if ($helper->ask($input, $output, $question)) {
$objectStore->deleteObject($object);
}
return 0;
return self::SUCCESS;
}
}

@ -31,10 +31,9 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class Get extends Command {
private ObjectUtil $objectUtils;
public function __construct(ObjectUtil $objectUtils) {
$this->objectUtils = $objectUtils;
public function __construct(
private ObjectUtil $objectUtils,
) {
parent::__construct();
}
@ -52,29 +51,29 @@ class Get extends Command {
$outputName = $input->getArgument('output');
$objectStore = $this->objectUtils->getObjectStore($input->getOption("bucket"), $output);
if (!$objectStore) {
return 1;
return self::FAILURE;
}
if (!$objectStore->objectExists($object)) {
$output->writeln("<error>Object $object does not exist</error>");
return 1;
} else {
try {
$source = $objectStore->readObject($object);
} catch (\Exception $e) {
$msg = $e->getMessage();
$output->writeln("<error>Failed to read $object from object store: $msg</error>");
return 1;
}
$target = $outputName === '-' ? STDOUT : fopen($outputName, 'w');
if (!$target) {
$output->writeln("<error>Failed to open $outputName for writing</error>");
return 1;
}
return self::FAILURE;
}
stream_copy_to_stream($source, $target);
return 0;
try {
$source = $objectStore->readObject($object);
} catch (\Exception $e) {
$msg = $e->getMessage();
$output->writeln("<error>Failed to read $object from object store: $msg</error>");
return self::FAILURE;
}
$target = $outputName === '-' ? STDOUT : fopen($outputName, 'w');
if (!$target) {
$output->writeln("<error>Failed to open $outputName for writing</error>");
return self::FAILURE;
}
stream_copy_to_stream($source, $target);
return self::SUCCESS;
}
}

@ -30,12 +30,10 @@ use OCP\IDBConnection;
use Symfony\Component\Console\Output\OutputInterface;
class ObjectUtil {
private IConfig $config;
private IDBConnection $connection;
public function __construct(IConfig $config, IDBConnection $connection) {
$this->config = $config;
$this->connection = $connection;
public function __construct(
private IConfig $config,
private IDBConnection $connection,
) {
}
private function getObjectStoreConfig(): ?array {
@ -50,9 +48,9 @@ class ObjectUtil {
$config['multibucket'] = false;
}
return $config;
} else {
return null;
}
return null;
}
public function getObjectStore(?string $bucket, OutputInterface $output): ?IObjectStore {
@ -91,20 +89,21 @@ class ObjectUtil {
* Check if an object is referenced in the database
*/
public function objectExistsInDb(string $object): int|false {
if (str_starts_with($object, 'urn:oid:')) {
$fileId = (int)substr($object, strlen('urn:oid:'));
$query = $this->connection->getQueryBuilder();
$query->select('fileid')
->from('filecache')
->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
$result = $query->executeQuery();
if ($result->fetchOne() !== false) {
return $fileId;
} else {
return false;
}
} else {
if (!str_starts_with($object, 'urn:oid:')) {
return false;
}
$fileId = (int)substr($object, strlen('urn:oid:'));
$query = $this->connection->getQueryBuilder();
$query->select('fileid')
->from('filecache')
->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
$result = $query->executeQuery();
if ($result->fetchOne() === false) {
return false;
}
return $fileId;
}
}

@ -33,12 +33,10 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
class Put extends Command {
private ObjectUtil $objectUtils;
private IMimeTypeDetector $mimeTypeDetector;
public function __construct(ObjectUtil $objectUtils, IMimeTypeDetector $mimeTypeDetector) {
$this->objectUtils = $objectUtils;
$this->mimeTypeDetector = $mimeTypeDetector;
public function __construct(
private ObjectUtil $objectUtils,
private IMimeTypeDetector $mimeTypeDetector,
) {
parent::__construct();
}
@ -75,10 +73,10 @@ class Put extends Command {
$source = $inputName === '-' ? STDIN : fopen($inputName, 'r');
if (!$source) {
$output->writeln("<error>Failed to open $inputName</error>");
return 1;
return self::FAILURE;
}
$objectStore->writeObject($object, $source, $this->mimeTypeDetector->detectPath($inputName));
return 0;
return self::SUCCESS;
}
}

@ -34,12 +34,10 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class Put extends Command {
private FileUtils $fileUtils;
private IRootFolder $rootFolder;
public function __construct(FileUtils $fileUtils, IRootFolder $rootFolder) {
$this->fileUtils = $fileUtils;
$this->rootFolder = $rootFolder;
public function __construct(
private FileUtils $fileUtils,
private IRootFolder $rootFolder,
) {
parent::__construct();
}
@ -58,29 +56,28 @@ class Put extends Command {
if ($node instanceof Folder) {
$output->writeln("<error>$fileOutput is a folder</error>");
return 1;
return self::FAILURE;
}
if (!$node and is_numeric($fileOutput)) {
$output->writeln("<error>$fileOutput not found</error>");
return 1;
return self::FAILURE;
}
$source = ($inputName === null || $inputName === '-') ? STDIN : fopen($inputName, 'r');
if (!$source) {
$output->writeln("<error>Failed to open $inputName</error>");
return 1;
return self::FAILURE;
}
if ($node instanceof File) {
$target = $node->fopen('w');
if (!$target) {
$output->writeln("<error>Failed to open $fileOutput</error>");
return 1;
return self::FAILURE;
}
stream_copy_to_stream($source, $target);
} else {
$this->rootFolder->newFile($fileOutput, $source);
}
return 0;
return self::SUCCESS;
}
}

@ -33,17 +33,13 @@ use Symfony\Component\Console\Output\OutputInterface;
class RepairTree extends Command {
public const CHUNK_SIZE = 200;
/**
* @var IDBConnection
*/
protected $connection;
public function __construct(IDBConnection $connection) {
$this->connection = $connection;
public function __construct(
protected IDBConnection $connection,
) {
parent::__construct();
}
protected function configure() {
protected function configure(): void {
$this
->setName('files:repair-tree')
->setDescription('Try and repair malformed filesystem tree structures')
@ -90,7 +86,7 @@ class RepairTree extends Command {
$this->connection->commit();
}
return 0;
return self::SUCCESS;
}
private function getFileId(int $storage, string $path) {
@ -102,7 +98,7 @@ class RepairTree extends Command {
return $query->execute()->fetch(\PDO::FETCH_COLUMN);
}
private function deleteById(int $fileId) {
private function deleteById(int $fileId): void {
$query = $this->connection->getQueryBuilder();
$query->delete('filecache')
->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId)));

@ -54,26 +54,20 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class Scan extends Base {
private IUserManager $userManager;
protected float $execTime = 0;
protected int $foldersCounter = 0;
protected int $filesCounter = 0;
protected int $errorsCounter = 0;
private IRootFolder $root;
private MetadataManager $metadataManager;
public function __construct(
IUserManager $userManager,
IRootFolder $rootFolder,
MetadataManager $metadataManager
private IUserManager $userManager,
private IRootFolder $rootFolder,
private MetadataManager $metadataManager
) {
$this->userManager = $userManager;
parent::__construct();
$this->root = $rootFolder;
$this->metadataManager = $metadataManager;
}
protected function configure() {
protected function configure(): void {
parent::configure();
$this
@ -134,7 +128,7 @@ class Scan extends Base {
++$this->filesCounter;
$this->abortIfInterrupted();
if ($scanMetadata) {
$node = $this->root->get($path);
$node = $this->rootFolder->get($path);
if ($node instanceof File) {
$this->metadataManager->generateMetadata($node, false);
}
@ -181,7 +175,7 @@ class Scan extends Base {
}
}
public function filterHomeMount(IMountPoint $mountPoint) {
public function filterHomeMount(IMountPoint $mountPoint): bool {
// any mountpoint inside '/$user/files/'
return substr_count($mountPoint->getMountPoint(), '/') <= 3;
}
@ -202,7 +196,7 @@ class Scan extends Base {
$users_total = count($users);
if ($users_total === 0) {
$output->writeln('<error>Please specify the user id to scan, --all to scan for all users or --path=...</error>');
return 1;
return self::FAILURE;
}
$this->initTools($output);
@ -212,7 +206,7 @@ class Scan extends Base {
if (is_object($user)) {
$user = $user->getUID();
}
$path = $inputPath ? $inputPath : '/' . $user;
$path = $inputPath ?: '/' . $user;
++$user_count;
if ($this->userManager->userExists($user)) {
$output->writeln("Starting scan for user $user_count out of $users_total ($user)");
@ -231,13 +225,13 @@ class Scan extends Base {
}
$this->presentStats($output);
return 0;
return self::SUCCESS;
}
/**
* Initialises some useful tools for the Command
*/
protected function initTools(OutputInterface $output) {
protected function initTools(OutputInterface $output): void {
// Start the timer
$this->execTime = -microtime(true);
// Convert PHP errors to exceptions
@ -270,10 +264,7 @@ class Scan extends Base {
return true;
}
/**
* @param OutputInterface $output
*/
protected function presentStats(OutputInterface $output) {
protected function presentStats(OutputInterface $output): void {
// Stop the timer
$this->execTime += microtime(true);
@ -299,11 +290,9 @@ class Scan extends Base {
/**
* Formats microtime into a human readable format
*
* @return string
* Formats microtime into a human-readable format
*/
protected function formatExecTime() {
protected function formatExecTime(): string {
$secs = (int)round($this->execTime);
# convert seconds into HH:MM:SS form
return sprintf('%02d:%02d:%02d', (int)($secs / 3600), ((int)($secs / 60) % 60), $secs % 60);

@ -46,26 +46,20 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ScanAppData extends Base {
protected float $execTime = 0;
/** @var IRootFolder */
protected $root;
/** @var IConfig */
protected $config;
/** @var float */
protected $execTime = 0;
/** @var int */
protected $foldersCounter = 0;
/** @var int */
protected $filesCounter = 0;
public function __construct(IRootFolder $rootFolder, IConfig $config) {
parent::__construct();
protected int $foldersCounter = 0;
protected int $filesCounter = 0;
$this->root = $rootFolder;
$this->config = $config;
public function __construct(
protected IRootFolder $rootFolder,
protected IConfig $config,
) {
parent::__construct();
}
protected function configure() {
protected function configure(): void {
parent::configure();
$this
@ -80,7 +74,7 @@ class ScanAppData extends Base {
$appData = $this->getAppDataFolder();
} catch (NotFoundException $e) {
$output->writeln('<error>NoAppData folder found</error>');
return 1;
return self::FAILURE;
}
if ($folder !== '') {
@ -88,7 +82,7 @@ class ScanAppData extends Base {
$appData = $appData->get($folder);
} catch (NotFoundException $e) {
$output->writeln('<error>Could not find folder: ' . $folder . '</error>');
return 1;
return self::FAILURE;
}
}
@ -126,21 +120,21 @@ class ScanAppData extends Base {
} catch (ForbiddenException $e) {
$output->writeln('<error>Storage not writable</error>');
$output->writeln('<info>Make sure you\'re running the scan command only as the user the web server runs as</info>');
return 1;
return self::FAILURE;
} catch (InterruptedException $e) {
# exit the function if ctrl-c has been pressed
$output->writeln('<info>Interrupted by user</info>');
return 1;
return self::FAILURE;
} catch (NotFoundException $e) {
$output->writeln('<error>Path not found: ' . $e->getMessage() . '</error>');
return 1;
return self::FAILURE;
} catch (\Exception $e) {
$output->writeln('<error>Exception during scan: ' . $e->getMessage() . '</error>');
$output->writeln('<error>' . $e->getTraceAsString() . '</error>');
return 1;
return self::FAILURE;
}
return 0;
return self::SUCCESS;
}
@ -167,7 +161,7 @@ class ScanAppData extends Base {
/**
* Initialises some useful tools for the Command
*/
protected function initTools() {
protected function initTools(): void {
// Start the timer
$this->execTime = -microtime(true);
// Convert PHP errors to exceptions
@ -186,7 +180,7 @@ class ScanAppData extends Base {
*
* @throws \ErrorException
*/
public function exceptionErrorHandler($severity, $message, $file, $line) {
public function exceptionErrorHandler($severity, $message, $file, $line): void {
if (!(error_reporting() & $severity)) {
// This error code is not included in error_reporting
return;
@ -194,10 +188,7 @@ class ScanAppData extends Base {
throw new \ErrorException($message, 0, $severity, $file, $line);
}
/**
* @param OutputInterface $output
*/
protected function presentStats(OutputInterface $output) {
protected function presentStats(OutputInterface $output): void {
// Stop the timer
$this->execTime += microtime(true);
@ -213,9 +204,8 @@ class ScanAppData extends Base {
*
* @param string[] $headers
* @param string[] $rows
* @param OutputInterface $output
*/
protected function showSummary($headers, $rows, OutputInterface $output) {
protected function showSummary($headers, $rows, OutputInterface $output): void {
$niceDate = $this->formatExecTime();
if (!$rows) {
$rows = [
@ -233,11 +223,9 @@ class ScanAppData extends Base {
/**
* Formats microtime into a human readable format
*
* @return string
* Formats microtime into a human-readable format
*/
protected function formatExecTime() {
protected function formatExecTime(): string {
$secs = round($this->execTime);
# convert seconds into HH:MM:SS form
return sprintf('%02d:%02d:%02d', (int)($secs / 3600), ((int)($secs / 60) % 60), (int)$secs % 60);
@ -273,6 +261,6 @@ class ScanAppData extends Base {
throw new NotFoundException();
}
return $this->root->get('appdata_'.$instanceId);
return $this->rootFolder->get('appdata_'.$instanceId);
}
}

@ -46,26 +46,15 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class TransferOwnership extends Command {
/** @var IUserManager */
private $userManager;
/** @var OwnershipTransferService */
private $transferService;
/** @var IConfig */
private $config;
public function __construct(IUserManager $userManager,
OwnershipTransferService $transferService,
IConfig $config) {
public function __construct(
private IUserManager $userManager,
private OwnershipTransferService $transferService,
private IConfig $config,
) {
parent::__construct();
$this->userManager = $userManager;
$this->transferService = $transferService;
$this->config = $config;
}
protected function configure() {
protected function configure(): void {
$this
->setName('files:transfer-ownership')
->setDescription('All files and folders are moved to another user - outgoing shares and incoming user file shares (optionally) are moved as well.')
@ -107,7 +96,7 @@ class TransferOwnership extends Command {
if ($input->getArgument(('source-user')) === $input->getArgument('destination-user')) {
$output->writeln("<error>Ownership can't be transferred when Source and Destination users are the same user. Please check your input.</error>");
return 1;
return self::FAILURE;
}
$sourceUserObject = $this->userManager->get($input->getArgument('source-user'));
@ -115,12 +104,12 @@ class TransferOwnership extends Command {
if (!$sourceUserObject instanceof IUser) {
$output->writeln("<error>Unknown source user " . $input->getArgument('source-user') . "</error>");
return 1;
return self::FAILURE;
}
if (!$destinationUserObject instanceof IUser) {
$output->writeln("<error>Unknown destination user " . $input->getArgument('destination-user') . "</error>");
return 1;
return self::FAILURE;
}
try {
@ -137,13 +126,12 @@ class TransferOwnership extends Command {
$includeIncoming = $this->config->getSystemValue('transferIncomingShares', false);
if (gettype($includeIncoming) !== 'boolean') {
$output->writeln("<error> config.php: 'transfer-incoming-shares': wrong usage. Transfer aborted.</error>");
return 1;
return self::FAILURE;
}
break;
default:
$output->writeln("<error>Option --transfer-incoming-shares: wrong usage. Transfer aborted.</error>");
return 1;
break;
return self::FAILURE;
}
$this->transferService->transfer(
@ -157,9 +145,9 @@ class TransferOwnership extends Command {
);
} catch (TransferOwnershipException $e) {
$output->writeln("<error>" . $e->getMessage() . "</error>");
return $e->getCode() !== 0 ? $e->getCode() : 1;
return $e->getCode() !== 0 ? $e->getCode() : self::FAILURE;
}
return 0;
return self::SUCCESS;
}
}

Loading…
Cancel
Save