Skip to content

Commit

Permalink
Fix getting consecutive storage info
Browse files Browse the repository at this point in the history
The issue is that OC_Helper is using Filesystem internally and
Filesystem, will only use the same View for each users, so we were
basically only calculating the size once when processing multiple users.

Signed-off-by: Carl Schwan <carl@carlschwan.eu>
  • Loading branch information
CarlSchwan committed May 13, 2022
1 parent bed70d1 commit 7e4f22e
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 32 deletions.
1 change: 1 addition & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
</background-jobs>
<commands>
<command>OCA\MonthlyStatusEmail\Command\SendMail</command>
<command>OCA\MonthlyStatusEmail\Command\SendAllMail</command>
</commands>
<settings>
<personal>OCA\MonthlyStatusEmail\Settings\PersonalSettings</personal>
Expand Down
80 changes: 80 additions & 0 deletions lib/Command/SendAllMail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php declare(strict_types=1);

/**
* @copyright Copyright 2021 Carl Schwan <carl@carlschwan.eu>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\MonthlyStatusEmail\Command;

use OCP\IUserManager;
use OCP\IUser;
use OC\Core\Command\Base;
use OCA\MonthlyStatusEmail\Service\MailSender;
use OCA\MonthlyStatusEmail\Service\NotificationTrackerService;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class SendAllMail extends Base {
/** @var IUserManager $userManager */
private $userManager;
/** @var NotificationTrackerService */
private $service;
/** @var MailSender */
private $mailSender;

public function __construct(
NotificationTrackerService $service,
IUserManager $userManager,
MailSender $mailSender
) {
parent::__construct();
$this->userManager = $userManager;
$this->service = $service;
$this->mailSender = $mailSender;
}

protected function configure() {
$this
->setName('monthly_status_email:send-all')
->setDescription('Send the notification mail to all users');
parent::configure();
}

protected function execute(InputInterface $input, OutputInterface $output) {
$this->userManager->callForSeenUsers(function (IUser $user) use ($output) {
$trackedNotification = $this->service->find($user->getUID());

$to = $user->getEMailAddress();
if ($to === null) {
// We don't have any email address, not sure what to do here.
$output->writeln('<error>User doesn\'t have an email address</error>');
return;
}
$ret = $this->mailSender->sendMonthlyMailTo($trackedNotification);
if ($ret) {
$output->writeln('Email sent to ' . ($user->getDisplayName() ?? $user->getUID()));
} else {
$output->writeln('Failure sending email to ' . $user->getDisplayName());
}
});
return 0;
}
}
7 changes: 5 additions & 2 deletions lib/Jobs/InitDatabaseJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@

namespace OCA\MonthlyStatusEmail\Jobs;

use OC\BackgroundJob\QueuedJob;
use OCA\MonthlyStatusEmail\Service\NotificationTrackerService;
use OCP\BackgroundJob\QueuedJob;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IUser;
use OCP\IUserManager;

Expand All @@ -43,8 +44,10 @@ class InitDatabaseJob extends QueuedJob {

public function __construct(
NotificationTrackerService $service,
IUserManager $userManager
IUserManager $userManager,
ITimeFactory $timeFactory
) {
parent::__construct($timeFactory);
$this->service = $service;
$this->userManager = $userManager;
}
Expand Down
3 changes: 1 addition & 2 deletions lib/Service/MailSender.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@

namespace OCA\MonthlyStatusEmail\Service;

use Icewind\SMB\IServer;
use OC\Files\FileInfo;
use OCP\Files\FileInfo;
use OCA\MonthlyStatusEmail\Db\NotificationTracker;
use OCP\DB\Exception;
use OCP\IConfig;
Expand Down
57 changes: 54 additions & 3 deletions lib/Service/StorageInfoProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,64 @@
namespace OCA\MonthlyStatusEmail\Service;

use OCP\IUser;
use OC\Files\View;

class StorageInfoProvider {
/**
* Calculate the disc space for the given path
*
* @throws \OCP\Files\NotFoundException
*/
public function getStorageInfo(IUser $user): array {
// make sure FS is setup before querying storage related stuff...
\OC_Util::setupFS($user->getUID());

// Handle storage specific events
return \OC_Helper::getStorageInfo('/');
// return storage info without adding mount points
$includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
$view = new View("/" . $user->getUID() . "/files");
$fullPath = $view->getAbsolutePath('');

$rootInfo = $view->getFileInfo('', 'ext');
if (!$rootInfo instanceof \OCP\Files\FileInfo) {
throw new \OCP\Files\NotFoundException();
}
$used = $rootInfo->getSize($includeMountPoints);
if ($used < 0) {
$used = 0;
}
$quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
$mount = $rootInfo->getMountPoint();
$storage = $mount->getStorage();
$sourceStorage = $storage;
$internalPath = $rootInfo->getInternalPath();
if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
/** @var \OC\Files\Storage\Wrapper\Quota $storage */
$quota = $sourceStorage->getQuota();
}

$free = $sourceStorage->free_space($internalPath);
if ($free >= 0) {
$total = $free + $used;
} else {
$total = $free; //either unknown or unlimited
}
if ($total > 0) {
if ($quota > 0 && $total > $quota) {
$total = $quota;
}
// prevent division by zero or error codes (negative values)
$relative = round(($used / $total) * 10000) / 100;
} else {
$relative = 0;
}

$info = [
'free' => $free,
'used' => $used,
'quota' => (int)$quota,
'total' => $total,
'relative' => $relative,
];

return $info;
}
}
3 changes: 3 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorBaseline="tests/psalm-baseline.xml"
>
<stubs>
<file name="tests/stub.phpstub" preloadClasses="true"/>
</stubs>
<projectFiles>
<directory name="lib" />
<ignoreFiles>
Expand Down
25 changes: 0 additions & 25 deletions tests/psalm-baseline.xml
Original file line number Diff line number Diff line change
@@ -1,35 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.x-dev@">
<file src="lib/Command/SendMail.php">
<UndefinedClass occurrences="1">
<code>Base</code>
</UndefinedClass>
</file>
<file src="lib/Jobs/InitDatabaseJob.php">
<UndefinedClass occurrences="1">
<code>QueuedJob</code>
</UndefinedClass>
</file>
<file src="lib/Migration/InitDatabase.php">
<MissingDependency occurrences="1">
<code>InitDatabaseJob</code>
</MissingDependency>
</file>
<file src="lib/Service/MailSender.php">
<UndefinedClass occurrences="1">
<code>FileInfo</code>
</UndefinedClass>
</file>
<file src="lib/Service/MessageProvider.php">
<NullArgument occurrences="1">
<code>null</code>
</NullArgument>
</file>
<file src="lib/Service/NoFileUploadedDetector.php">
<UndefinedClass occurrences="1">
<code>View</code>
</UndefinedClass>
</file>
<file src="lib/Service/NotificationTrackerService.php">
<InvalidReturnType occurrences="3">
<code>NotificationTracker</code>
Expand Down
101 changes: 101 additions & 0 deletions tests/stub.phpstub
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

namespace {
use OCP\IServerContainer;

class OC {
static $CLI = false;
/** @var IServerContainer */
static $server;
}
}

namespace OC\Files\Node {
use OCP\Files\FileInfo;
abstract class Node implements \OCP\Files\Node {
/** @return FileInfo|\ArrayAccess */
public function getFileInfo() {}

/** @return \OCP\Files\Mount\IMountPoint */
public function getMountPoint() {}
}
}

namespace OC\Hooks {
class Emitter {
public function emit(string $class, string $value, array $option) {}
/** Closure $closure */
public function listen(string $class, string $value, $closure) {}
}
class BasicEmitter extends Emitter {
}
}

namespace OC\Core\Command {
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class Base {
public const OUTPUT_FORMAT_PLAIN = 'plain';
public const OUTPUT_FORMAT_JSON = 'json';
public const OUTPUT_FORMAT_JSON_PRETTY = 'json_pretty';

public function __construct() {}
protected function configure() {}
public function run(InputInterface $input, OutputInterface $output) {}
public function setName(string $name) {}
public function getHelper(string $name) {}
protected function writeArrayInOutputFormat(InputInterface $input, OutputInterface $output, $items, $prefix = ' - ') {
}
}
}

namespace OC\Files\ObjectStore {
class NoopScanner {}
}

namespace Symfony\Component\Console\Helper {
use Symfony\Component\Console\Output\OutputInterface;
class Table {
public function __construct(OutputInterface $text) {}
public function setHeaders(array $header) {}
public function setRows(array $rows) {}
public function render() {}
}
}

namespace Symfony\Component\Console\Input {
class InputInterface {
public function getOption(string $key) {}
public function getArgument(string $key) {}
}
class InputArgument {
const REQUIRED = 0;
const OPTIONAL = 1;
const IS_ARRAY = 1;
}
class InputOption {
const VALUE_NONE = 1;
const VALUE_REQUIRED = 1;
}
}

namespace Symfony\Component\Console\Question {
class ConfirmationQuestion {
public function __construct(string $text, bool $default) {}
}
}

namespace Symfony\Component\Console\Output {
class OutputInterface {
public const VERBOSITY_VERBOSE = 1;
public function writeln(string $text, int $flat = 0) {}
}
}

namespace OC\Files {
class View {
public function __construct(string $path) {}
public function unlink($path) {}
public function getDirectoryContent($path) {}
}
}

0 comments on commit 7e4f22e

Please sign in to comment.