Browse Source

Introduce BackendService for managing external storage backends

Backends are registered to the BackendService through new data
structures:

Backends are concrete classes, deriving from
\OCA\Files_External\Lib\Backend\Backend. During construction, the
various configuration parameters of the Backend can be set, in a design
similar to Symfony Console.

DefinitionParameter stores a parameter configuration for an external
storage: name of parameter, human-readable name, type of parameter
(text, password, hidden, checkbox), flags (optional or not).

Storages in the StoragesController now get their parameters validated
server-side (fixes a TODO).
tags/v8.2beta1
Robin McCorkell 8 years ago
parent
commit
37beb58c6f
31 changed files with 1638 additions and 558 deletions
  1. 9
    4
      apps/files_external/appinfo/app.php
  2. 15
    0
      apps/files_external/appinfo/application.php
  3. 25
    16
      apps/files_external/controller/globalstoragescontroller.php
  4. 63
    6
      apps/files_external/controller/storagescontroller.php
  5. 28
    14
      apps/files_external/controller/userstoragescontroller.php
  6. 94
    0
      apps/files_external/lib/backend/backend.php
  7. 66
    189
      apps/files_external/lib/config.php
  8. 12
    2
      apps/files_external/lib/config/configadapter.php
  9. 179
    0
      apps/files_external/lib/definitionparameter.php
  10. 86
    0
      apps/files_external/lib/dependencytrait.php
  11. 147
    0
      apps/files_external/lib/frontenddefinitiontrait.php
  12. 64
    0
      apps/files_external/lib/missingdependency.php
  13. 39
    6
      apps/files_external/lib/personalmount.php
  14. 60
    0
      apps/files_external/lib/prioritytrait.php
  15. 31
    14
      apps/files_external/lib/storageconfig.php
  16. 51
    0
      apps/files_external/lib/storagemodifiertrait.php
  17. 129
    0
      apps/files_external/lib/visibilitytrait.php
  18. 10
    24
      apps/files_external/personal.php
  19. 170
    0
      apps/files_external/service/backendservice.php
  20. 64
    4
      apps/files_external/service/storagesservice.php
  21. 4
    0
      apps/files_external/service/userstoragesservice.php
  22. 12
    39
      apps/files_external/settings.php
  23. 133
    82
      apps/files_external/templates/settings.php
  24. 3
    1
      apps/files_external/tests/controller/globalstoragescontrollertest.php
  25. 60
    1
      apps/files_external/tests/controller/storagescontrollertest.php
  26. 17
    34
      apps/files_external/tests/controller/userstoragescontrollertest.php
  27. 0
    104
      apps/files_external/tests/dynamicmountconfig.php
  28. 14
    12
      apps/files_external/tests/service/globalstoragesservicetest.php
  29. 44
    3
      apps/files_external/tests/service/storagesservicetest.php
  30. 2
    2
      apps/files_external/tests/service/userstoragesservicetest.php
  31. 7
    1
      apps/files_external/tests/storageconfigtest.php

+ 9
- 4
apps/files_external/appinfo/app.php View File

@@ -30,9 +30,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
$app = new \OCA\Files_external\Appinfo\Application();

$l = \OC::$server->getL10N('files_external');

OC::$CLASSPATH['OC\Files\Storage\StreamWrapper'] = 'files_external/lib/streamwrapper.php';
OC::$CLASSPATH['OC\Files\Storage\FTP'] = 'files_external/lib/ftp.php';
@@ -50,6 +47,11 @@ OC::$CLASSPATH['OCA\Files\External\Api'] = 'files_external/lib/api.php';

require_once __DIR__ . '/../3rdparty/autoload.php';

$app = new \OCA\Files_external\Appinfo\Application();
$appContainer = $app->getContainer();

$l = \OC::$server->getL10N('files_external');

OCP\App::registerAdmin('files_external', 'settings');
if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == 'yes') {
OCP\App::registerPersonal('files_external', 'personal');
@@ -63,6 +65,9 @@ if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == '
"name" => $l->t('External storage')
]);

// Teach OC_Mount_Config about the AppFramework
\OC_Mount_Config::initApp($appContainer);

// connecting hooks
OCP\Util::connectHook('OC_Filesystem', 'post_initMountPoints', '\OC_Mount_Config', 'initMountPointsHook');
OCP\Util::connectHook('OC_User', 'post_login', 'OC\Files\Storage\SMB_OC', 'login');
@@ -237,5 +242,5 @@ OC_Mount_Config::registerBackend('\OC\Files\Storage\SFTP_Key', [
'custom' => 'sftp_key',
]
);
$mountProvider = new \OCA\Files_External\Config\ConfigAdapter();
$mountProvider = $appContainer->query('OCA\Files_External\Config\ConfigAdapter');
\OC::$server->getMountProviderCollection()->registerProvider($mountProvider);

+ 15
- 0
apps/files_external/appinfo/application.php View File

@@ -3,6 +3,7 @@
* @author Morris Jobke <hey@morrisjobke.de>
* @author Ross Nicoll <jrn@jrn.me.uk>
* @author Vincent Petry <pvince81@owncloud.com>
* @author Robin McCorkell <rmccorkell@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
@@ -26,6 +27,9 @@ namespace OCA\Files_External\AppInfo;
use \OCA\Files_External\Controller\AjaxController;
use \OCP\AppFramework\App;
use \OCP\IContainer;
use \OCA\Files_External\Service\BackendService;
use \OCA\Files_External\Lib\BackendConfig;
use \OCA\Files_External\Lib\BackendParameter;

/**
* @package OCA\Files_External\Appinfo
@@ -45,5 +49,16 @@ class Application extends App {
$c->query('Request')
);
});

$this->loadBackends();
}

/**
* Load storage backends provided by this app
*/
protected function loadBackends() {
$container = $this->getContainer();
$service = $container->query('OCA\\Files_External\\Service\\BackendService');
}

}

+ 25
- 16
apps/files_external/controller/globalstoragescontroller.php View File

@@ -81,14 +81,18 @@ class GlobalStoragesController extends StoragesController {
$applicableGroups,
$priority
) {
$newStorage = new StorageConfig();
$newStorage->setMountPoint($mountPoint);
$newStorage->setBackendClass($backendClass);
$newStorage->setBackendOptions($backendOptions);
$newStorage->setMountOptions($mountOptions);
$newStorage->setApplicableUsers($applicableUsers);
$newStorage->setApplicableGroups($applicableGroups);
$newStorage->setPriority($priority);
$newStorage = $this->createStorage(
$mountPoint,
$backendClass,
$backendOptions,
$mountOptions,
$applicableUsers,
$applicableGroups,
$priority
);
if ($newStorage instanceof DataResponse) {
return $newStorage;
}

$response = $this->validate($newStorage);
if (!empty($response)) {
@@ -129,14 +133,19 @@ class GlobalStoragesController extends StoragesController {
$applicableGroups,
$priority
) {
$storage = new StorageConfig($id);
$storage->setMountPoint($mountPoint);
$storage->setBackendClass($backendClass);
$storage->setBackendOptions($backendOptions);
$storage->setMountOptions($mountOptions);
$storage->setApplicableUsers($applicableUsers);
$storage->setApplicableGroups($applicableGroups);
$storage->setPriority($priority);
$storage = $this->createStorage(
$mountPoint,
$backendClass,
$backendOptions,
$mountOptions,
$applicableUsers,
$applicableGroups,
$priority
);
if ($storage instanceof DataResponse) {
return $storage;
}
$storage->setId($id);

$response = $this->validate($storage);
if (!empty($response)) {

+ 63
- 6
apps/files_external/controller/storagescontroller.php View File

@@ -32,6 +32,7 @@ use \OCP\AppFramework\Http;
use \OCA\Files_external\Service\StoragesService;
use \OCA\Files_external\NotFoundException;
use \OCA\Files_external\Lib\StorageConfig;
use \OCA\Files_External\Lib\Backend\Backend;

/**
* Base class for storages controllers
@@ -71,6 +72,48 @@ abstract class StoragesController extends Controller {
$this->service = $storagesService;
}

/**
* Create a storage from its parameters
*
* @param string $mountPoint storage mount point
* @param string $backendClass backend class name
* @param array $backendOptions backend-specific options
* @param array|null $mountOptions mount-specific options
* @param array|null $applicableUsers users for which to mount the storage
* @param array|null $applicableGroups groups for which to mount the storage
* @param int|null $priority priority
*
* @return StorageConfig|DataResponse
*/
protected function createStorage(
$mountPoint,
$backendClass,
$backendOptions,
$mountOptions = null,
$applicableUsers = null,
$applicableGroups = null,
$priority = null
) {
try {
return $this->service->createStorage(
$mountPoint,
$backendClass,
$backendOptions,
$mountOptions,
$applicableUsers,
$applicableGroups,
$priority
);
} catch (\InvalidArgumentException $e) {
return new DataResponse(
[
'message' => (string)$this->l10n->t('Invalid backend class "%s"', [$backendClass])
],
Http::STATUS_UNPROCESSABLE_ENTITY
);
}
}

/**
* Validate storage config
*
@@ -89,14 +132,24 @@ abstract class StoragesController extends Controller {
);
}

// TODO: validate that other attrs are set

$backends = \OC_Mount_Config::getBackends();
if (!isset($backends[$storage->getBackendClass()])) {
/** @var Backend */
$backend = $storage->getBackend();
if (!$backend || $backend->checkDependencies()) {
// invalid backend
return new DataResponse(
array(
'message' => (string)$this->l10n->t('Invalid storage backend "%s"', array($storage->getBackendClass()))
'message' => (string)$this->l10n->t('Invalid storage backend "%s"', [
$storage->getBackend()->getClass()
])
),
Http::STATUS_UNPROCESSABLE_ENTITY
);
}
if (!$backend->validateStorage($storage)) {
// unsatisfied parameters
return new DataResponse(
array(
'message' => (string)$this->l10n->t('Unsatisfied backend parameters')
),
Http::STATUS_UNPROCESSABLE_ENTITY
);
@@ -114,10 +167,14 @@ abstract class StoragesController extends Controller {
* @param StorageConfig $storage storage configuration
*/
protected function updateStorageStatus(StorageConfig &$storage) {
/** @var Backend */
$backend = $storage->getBackend();
$backend->manipulateStorageConfig($storage);

// update status (can be time-consuming)
$storage->setStatus(
\OC_Mount_Config::getBackendStatus(
$storage->getBackendClass(),
$storage->getBackend()->getStorageClass(),
$storage->getBackendOptions(),
false
)

+ 28
- 14
apps/files_external/controller/userstoragescontroller.php View File

@@ -30,8 +30,10 @@ use \OCP\AppFramework\Http\DataResponse;
use \OCP\AppFramework\Controller;
use \OCP\AppFramework\Http;
use \OCA\Files_external\Service\UserStoragesService;
use \OCA\Files_External\Service\BackendService;
use \OCA\Files_external\NotFoundException;
use \OCA\Files_external\Lib\StorageConfig;
use \OCA\Files_External\Lib\Backend\Backend;

/**
* User storages controller
@@ -69,17 +71,20 @@ class UserStoragesController extends StoragesController {
protected function validate(StorageConfig $storage) {
$result = parent::validate($storage);

if ($result != null) {
if ($result !== null) {
return $result;
}

// Verify that the mount point applies for the current user
// Prevent non-admin users from mounting local storage and other disabled backends
$allowedBackends = \OC_Mount_Config::getPersonalBackends();
if (!isset($allowedBackends[$storage->getBackendClass()])) {
/** @var Backend */
$backend = $storage->getBackend();
if (!$backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) {
return new DataResponse(
array(
'message' => (string)$this->l10n->t('Invalid storage backend "%s"', array($storage->getBackendClass()))
'message' => (string)$this->l10n->t('Admin-only storage backend "%s"', [
$storage->getBackend()->getClass()
])
),
Http::STATUS_UNPROCESSABLE_ENTITY
);
@@ -117,11 +122,15 @@ class UserStoragesController extends StoragesController {
$backendOptions,
$mountOptions
) {
$newStorage = new StorageConfig();
$newStorage->setMountPoint($mountPoint);
$newStorage->setBackendClass($backendClass);
$newStorage->setBackendOptions($backendOptions);
$newStorage->setMountOptions($mountOptions);
$newStorage = $this->createStorage(
$mountPoint,
$backendClass,
$backendOptions,
$mountOptions
);
if ($newStorage instanceOf DataResponse) {
return $newStorage;
}

$response = $this->validate($newStorage);
if (!empty($response)) {
@@ -157,11 +166,16 @@ class UserStoragesController extends StoragesController {
$backendOptions,
$mountOptions
) {
$storage = new StorageConfig($id);
$storage->setMountPoint($mountPoint);
$storage->setBackendClass($backendClass);
$storage->setBackendOptions($backendOptions);
$storage->setMountOptions($mountOptions);
$storage = $this->createStorage(
$mountPoint,
$backendClass,
$backendOptions,
$mountOptions
);
if ($storage instanceOf DataResponse) {
return $storage;
}
$storage->setId($id);

$response = $this->validate($storage);
if (!empty($response)) {

+ 94
- 0
apps/files_external/lib/backend/backend.php View File

@@ -0,0 +1,94 @@
<?php
/**
* @author Robin McCorkell <rmccorkell@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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/>
*
*/

namespace OCA\Files_External\Lib\Backend;

use \OCA\Files_External\Lib\StorageConfig;
use \OCA\Files_External\Lib\VisibilityTrait;
use \OCA\Files_External\Lib\FrontendDefinitionTrait;
use \OCA\Files_External\Lib\PriorityTrait;
use \OCA\Files_External\Lib\DependencyTrait;
use \OCA\Files_External\Lib\StorageModifierTrait;

/**
* Storage backend
*/
class Backend implements \JsonSerializable {

use VisibilityTrait;
use FrontendDefinitionTrait;
use PriorityTrait;
use DependencyTrait;
use StorageModifierTrait;

/** @var string storage class */
private $storageClass;

/**
* @return string
*/
public function getClass() {
// return storage class for legacy compat
return $this->getStorageClass();
}

/**
* @return string
*/
public function getStorageClass() {
return $this->storageClass;
}

/**
* @param string $class
* @return self
*/
public function setStorageClass($class) {
$this->storageClass = $class;
return $this;
}

/**
* Serialize into JSON for client-side JS
*
* @return array
*/
public function jsonSerialize() {
$data = $this->jsonSerializeDefinition();

$data['backend'] = $data['name']; // legacy compat
$data['priority'] = $this->getPriority();

return $data;
}

/**
* Check if parameters are satisfied in a StorageConfig
*
* @param StorageConfig $storage
* @return bool
*/
public function validateStorage(StorageConfig $storage) {
return $this->validateStorageDefinition($storage);
}

}


+ 66
- 189
apps/files_external/lib/config.php View File

@@ -32,6 +32,9 @@
*/

use phpseclib\Crypt\AES;
use \OCP\AppFramework\IAppContainer;
use \OCA\Files_External\Lib\BackendConfig;
use \OCA\Files_External\Service\BackendService;

/**
* Class to configure mount.json globally and for users
@@ -51,71 +54,19 @@ class OC_Mount_Config {
// whether to skip backend test (for unit tests, as this static class is not mockable)
public static $skipTest = false;

private static $backends = array();
/** @var IAppContainer */
private static $appContainer;

/**
* @param string $class
* @param array $definition
* @return bool
*/
public static function registerBackend($class, $definition) {
if (!isset($definition['backend'])) {
return false;
}

OC_Mount_Config::$backends[$class] = $definition;
return true;
}

/**
* Setup backends
* Teach OC_Mount_Config about the AppFramework
*
* @return array of previously registered backends
* @param IAppContainer $appContainer
*/
public static function setUp($backends = array()) {
$backup = self::$backends;
self::$backends = $backends;

return $backup;
}

/**
* Get details on each of the external storage backends, used for the mount config UI
* If a custom UI is needed, add the key 'custom' and a javascript file with that name will be loaded
* If the configuration parameter should be secret, add a '*' to the beginning of the value
* If the configuration parameter is a boolean, add a '!' to the beginning of the value
* If the configuration parameter is optional, add a '&' to the beginning of the value
* If the configuration parameter is hidden, add a '#' to the beginning of the value
*
* @return array
*/
public static function getBackends() {
$sortFunc = function ($a, $b) {
return strcasecmp($a['backend'], $b['backend']);
};

$backEnds = array();

foreach (OC_Mount_Config::$backends as $class => $backend) {
if (isset($backend['has_dependencies']) and $backend['has_dependencies'] === true) {
if (!method_exists($class, 'checkDependencies')) {
\OCP\Util::writeLog('files_external',
"Backend class $class has dependencies but doesn't provide method checkDependencies()",
\OCP\Util::DEBUG);
continue;
} elseif ($class::checkDependencies() !== true) {
continue;
}
}
$backEnds[$class] = $backend;
}

uasort($backEnds, $sortFunc);

return $backEnds;
public static function initApp(IAppContainer $appContainer) {
self::$appContainer = $appContainer;
}

/**
/*
* Hook that mounts the given user's visible mount points
*
* @param array $data
@@ -151,14 +102,14 @@ class OC_Mount_Config {
/**
* Returns the mount points for the given user.
* The mount point is relative to the data directory.
* TODO: Move me into StoragesService
*
* @param string $user user
* @return array of mount point string as key, mountpoint config as value
*/
public static function getAbsoluteMountPoints($user) {
$mountPoints = array();

$backends = self::getBackends();
$backendService = self::$appContainer->query('OCA\Files_External\Service\BackendService');

// Load system mount points
$mountConfig = self::readData();
@@ -166,18 +117,20 @@ class OC_Mount_Config {
// Global mount points (is this redundant?)
if (isset($mountConfig[self::MOUNT_TYPE_GLOBAL])) {
foreach ($mountConfig[self::MOUNT_TYPE_GLOBAL] as $mountPoint => $options) {
$backend = $backendService->getBackend($options['class']);
$options['personal'] = false;
$options['options'] = self::decryptPasswords($options['options']);
if (!isset($options['priority'])) {
$options['priority'] = $backends[$options['class']]['priority'];
$options['priority'] = $backend->getPriority();
}


// Override if priority greater
if ((!isset($mountPoints[$mountPoint]))
|| ($options['priority'] >= $mountPoints[$mountPoint]['priority'])
) {
$options['priority_type'] = self::MOUNT_TYPE_GLOBAL;
$options['backend'] = $backends[$options['class']]['backend'];
$options['backend'] = $backend->getText();
$mountPoints[$mountPoint] = $options;
}
}
@@ -190,10 +143,11 @@ class OC_Mount_Config {
foreach ($options as &$option) {
$option = self::setUserVars($user, $option);
}
$backend = $backendService->getBackend($options['class']);
$options['personal'] = false;
$options['options'] = self::decryptPasswords($options['options']);
if (!isset($options['priority'])) {
$options['priority'] = $backends[$options['class']]['priority'];
$options['priority'] = $backend->getPriority();
}

// Override if priority greater
@@ -201,7 +155,7 @@ class OC_Mount_Config {
|| ($options['priority'] >= $mountPoints[$mountPoint]['priority'])
) {
$options['priority_type'] = self::MOUNT_TYPE_GLOBAL;
$options['backend'] = $backends[$options['class']]['backend'];
$options['backend'] = $backend->getText();
$mountPoints[$mountPoint] = $options;
}
}
@@ -215,10 +169,11 @@ class OC_Mount_Config {
foreach ($options as &$option) {
$option = self::setUserVars($user, $option);
}
$backend = $backendService->getBackend($options['class']);
$options['personal'] = false;
$options['options'] = self::decryptPasswords($options['options']);
if (!isset($options['priority'])) {
$options['priority'] = $backends[$options['class']]['priority'];
$options['priority'] = $backend->getPriority();
}

// Override if priority greater or if priority type different
@@ -227,7 +182,7 @@ class OC_Mount_Config {
|| ($mountPoints[$mountPoint]['priority_type'] !== self::MOUNT_TYPE_GROUP)
) {
$options['priority_type'] = self::MOUNT_TYPE_GROUP;
$options['backend'] = $backends[$options['class']]['backend'];
$options['backend'] = $backend->getText();
$mountPoints[$mountPoint] = $options;
}
}
@@ -243,10 +198,11 @@ class OC_Mount_Config {
foreach ($options as &$option) {
$option = self::setUserVars($user, $option);
}
$backend = $backendService->getBackend($options['class']);
$options['personal'] = false;
$options['options'] = self::decryptPasswords($options['options']);
if (!isset($options['priority'])) {
$options['priority'] = $backends[$options['class']]['priority'];
$options['priority'] = $backend->getPriority();
}

// Override if priority greater or if priority type different
@@ -255,7 +211,7 @@ class OC_Mount_Config {
|| ($mountPoints[$mountPoint]['priority_type'] !== self::MOUNT_TYPE_USER)
) {
$options['priority_type'] = self::MOUNT_TYPE_USER;
$options['backend'] = $backends[$options['class']]['backend'];
$options['backend'] = $backend->getText();
$mountPoints[$mountPoint] = $options;
}
}
@@ -263,19 +219,18 @@ class OC_Mount_Config {
}
}

$personalBackends = self::getPersonalBackends();

// Load personal mount points
$mountConfig = self::readData($user);
if (isset($mountConfig[self::MOUNT_TYPE_USER][$user])) {
foreach ($mountConfig[self::MOUNT_TYPE_USER][$user] as $mountPoint => $options) {
if (isset($personalBackends[$options['class']])) {
$backend = $backendService->getBackend($options['class']);
if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) {
$options['personal'] = true;
$options['options'] = self::decryptPasswords($options['options']);

// Always override previous config
$options['priority_type'] = self::MOUNT_TYPE_PERSONAL;
$options['backend'] = $backends[$options['class']]['backend'];
$options['backend'] = $backend->getText();
$mountPoints[$mountPoint] = $options;
}
}
@@ -304,43 +259,6 @@ class OC_Mount_Config {
return $input;
}


/**
* Get details on each of the external storage backends, used for the mount config UI
* Some backends are not available as a personal backend, f.e. Local and such that have
* been disabled by the admin.
*
* If a custom UI is needed, add the key 'custom' and a javascript file with that name will be loaded
* If the configuration parameter should be secret, add a '*' to the beginning of the value
* If the configuration parameter is a boolean, add a '!' to the beginning of the value
* If the configuration parameter is optional, add a '&' to the beginning of the value
* If the configuration parameter is hidden, add a '#' to the beginning of the value
*
* @return array
*/
public static function getPersonalBackends() {

// Check whether the user has permissions to add personal storage backends
// return an empty array if this is not the case
if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') !== 'yes') {
return array();
}

$backEnds = self::getBackends();

// Remove local storage and other disabled storages
unset($backEnds['\OC\Files\Storage\Local']);

$allowedBackEnds = explode(',', OCP\Config::getAppValue('files_external', 'user_mounting_backends', ''));
foreach ($backEnds as $backend => $null) {
if (!in_array($backend, $allowedBackEnds)) {
unset($backEnds[$backend]);
}
}

return $backEnds;
}

/**
* Get the system mount points
* The returned array is not in the same format as getUserMountPoints()
@@ -349,7 +267,7 @@ class OC_Mount_Config {
*/
public static function getSystemMountPoints() {
$mountPoints = self::readData();
$backends = self::getBackends();
$backendService = self::$appContainer->query('\OCA\Files_External\Service\BackendService');
$system = array();
if (isset($mountPoints[self::MOUNT_TYPE_GROUP])) {
foreach ($mountPoints[self::MOUNT_TYPE_GROUP] as $group => $mounts) {
@@ -358,9 +276,10 @@ class OC_Mount_Config {
if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
$mount['class'] = '\OC\Files\Storage\\' . substr($mount['class'], 15);
}
$backend = $backendService->getBackend($mount['class']);
$mount['options'] = self::decryptPasswords($mount['options']);
if (!isset($mount['priority'])) {
$mount['priority'] = $backends[$mount['class']]['priority'];
$mount['priority'] = $backend->getPriority();
}
// Remove '/$user/files/' from mount point
$mountPoint = substr($mountPoint, 13);
@@ -368,7 +287,7 @@ class OC_Mount_Config {
$config = array(
'class' => $mount['class'],
'mountpoint' => $mountPoint,
'backend' => $backends[$mount['class']]['backend'],
'backend' => $backend->getText(),
'priority' => $mount['priority'],
'options' => $mount['options'],
'applicable' => array('groups' => array($group), 'users' => array())
@@ -401,16 +320,17 @@ class OC_Mount_Config {
if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
$mount['class'] = '\OC\Files\Storage\\' . substr($mount['class'], 15);
}
$backend = $backendService->getBackend($mount['class']);
$mount['options'] = self::decryptPasswords($mount['options']);
if (!isset($mount['priority'])) {
$mount['priority'] = $backends[$mount['class']]['priority'];
$mount['priority'] = $backend->getPriority();
}
// Remove '/$user/files/' from mount point
$mountPoint = substr($mountPoint, 13);
$config = array(
'class' => $mount['class'],
'mountpoint' => $mountPoint,
'backend' => $backends[$mount['class']]['backend'],
'backend' => $backend->getText(),
'priority' => $mount['priority'],
'options' => $mount['options'],
'applicable' => array('groups' => array(), 'users' => array($user))
@@ -447,7 +367,7 @@ class OC_Mount_Config {
*/
public static function getPersonalMountPoints() {
$mountPoints = self::readData(OCP\User::getUser());
$backEnds = self::getBackends();
$backendService = self::$appContainer->query('\OCA\Files_External\Service\BackendService');
$uid = OCP\User::getUser();
$personal = array();
if (isset($mountPoints[self::MOUNT_TYPE_USER][$uid])) {
@@ -456,12 +376,13 @@ class OC_Mount_Config {
if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
$mount['class'] = '\OC\Files\Storage\\' . substr($mount['class'], 15);
}
$backend = $backendService->getBackend($mount['class']);
$mount['options'] = self::decryptPasswords($mount['options']);
$config = array(
'class' => $mount['class'],
// Remove '/uid/files/' from mount point
'mountpoint' => substr($mountPoint, strlen($uid) + 8),
'backend' => $backEnds[$mount['class']]['backend'],
'backend' => $backend->getText(),
'options' => $mount['options']
);
if (isset($mount['id'])) {
@@ -535,7 +456,7 @@ class OC_Mount_Config {
$applicable,
$isPersonal = false,
$priority = null) {
$backends = self::getBackends();
$backendService = self::$appContainer->query('\OCA\Files_External\Service\BackendService');
$mountPoint = OC\Files\Filesystem::normalizePath($mountPoint);
$relMountPoint = $mountPoint;
if ($mountPoint === '' || $mountPoint === '/') {
@@ -543,15 +464,15 @@ class OC_Mount_Config {
return false;
}

if (!isset($backends[$class])) {
$backend = $backendService->getBackend($class);
if (!isset($backend)) {
// invalid backend
return false;
}
if ($isPersonal) {
// Verify that the mount point applies for the current user
// Prevent non-admin users from mounting local storage and other disabled backends
$allowed_backends = self::getPersonalBackends();
if ($applicable != OCP\User::getUser() || !isset($allowed_backends[$class])) {
if ($applicable != OCP\User::getUser() || !$backend->isVisibleFor(BackendConfig::VISIBILITY_PERSONAL)) {
return false;
}
$mountPoint = '/' . $applicable . '/files/' . ltrim($mountPoint, '/');
@@ -578,13 +499,8 @@ class OC_Mount_Config {

// Set default priority if none set
if (!isset($mountPoints[$mountType][$applicable][$mountPoint]['priority'])) {
if (isset($backends[$class]['priority'])) {
$mountPoints[$mountType][$applicable][$mountPoint]['priority']
= $backends[$class]['priority'];
} else {
$mountPoints[$mountType][$applicable][$mountPoint]['priority']
= 100;
}
$mountPoints[$mountType][$applicable][$mountPoint]['priority']
= $backend->getPriority();
}

self::writeData($isPersonal ? OCP\User::getUser() : null, $mountPoints);
@@ -721,74 +637,35 @@ class OC_Mount_Config {
}

/**
* check dependencies
* Get backend dependency message
* TODO: move into AppFramework along with templates
*
* @param BackendConfig[] $backends
* @return string
*/
public static function checkDependencies() {
$dependencies = array();
foreach (OC_Mount_Config::$backends as $class => $backend) {
if (isset($backend['has_dependencies']) and $backend['has_dependencies'] === true) {
$result = $class::checkDependencies();
if ($result !== true) {
if (!is_array($result)) {
$result = array($result);
}
foreach ($result as $key => $value) {
if (is_numeric($key)) {
OC_Mount_Config::addDependency($dependencies, $value, $backend['backend']);
} else {
OC_Mount_Config::addDependency($dependencies, $key, $backend['backend'], $value);
}
}
}
}
}

if (count($dependencies) > 0) {
return OC_Mount_Config::generateDependencyMessage($dependencies);
}
return '';
}

private static function addDependency(&$dependencies, $module, $backend, $message = null) {
if (!isset($dependencies[$module])) {
$dependencies[$module] = array();
}

if ($message === null) {
$dependencies[$module][] = $backend;
} else {
$dependencies[$module][] = array('backend' => $backend, 'message' => $message);
}
}

private static function generateDependencyMessage($dependencies) {
public static function dependencyMessage($backends) {
$l = new \OC_L10N('files_external');
$dependencyMessage = '';
foreach ($dependencies as $module => $backends) {
$dependencyGroup = array();
foreach ($backends as $backend) {
if (is_array($backend)) {
$dependencyMessage .= '<br />' . $l->t('<b>Note:</b> ') . $backend['message'];
$message = '';
$dependencyGroups = [];

foreach ($backends as $backend) {
foreach ($backend->checkDependencies() as $dependency) {
if ($message = $dependency->getMessage()) {
$message .= '<br />' . $l->t('<b>Note:</b> ') . $message;
} else {
$dependencyGroup[] = $backend;
$dependencyGroups[$dependency->getDependency()][] = $backend;
}
}
}

$dependencyGroupCount = count($dependencyGroup);
if ($dependencyGroupCount > 0) {
$backends = '';
for ($i = 0; $i < $dependencyGroupCount; $i++) {
if ($i > 0 && $i === $dependencyGroupCount - 1) {
$backends .= ' ' . $l->t('and') . ' ';
} elseif ($i > 0) {
$backends .= ', ';
}
$backends .= '<i>' . $dependencyGroup[$i] . '</i>';
}
$dependencyMessage .= '<br />' . OC_Mount_Config::getSingleDependencyMessage($l, $module, $backends);
}
foreach ($dependencyGroups as $module => $dependants) {
$backends = implode(', ', array_map(function($backend) {
return '<i>' . $backend->getText() . '</i>';
}, $dependants));
$message .= '<br />' . OC_Mount_Config::getSingleDependencyMessage($l, $module, $backends);
}
return $dependencyMessage;

return $message;
}

/**

+ 12
- 2
apps/files_external/lib/config/configadapter.php View File

@@ -24,14 +24,23 @@ namespace OCA\Files_External\Config;

use OC\Files\Mount\MountPoint;
use OCP\Files\Storage\IStorageFactory;
use OCA\Files_External\PersonalMount;
use OCA\Files_External\Lib\PersonalMount;
use OCP\Files\Config\IMountProvider;
use OCP\IUser;
use OCA\Files_external\Service\UserStoragesService;

/**
* Make the old files_external config work with the new public mount config api
*/
class ConfigAdapter implements IMountProvider {

/**
* @param UserStoragesService $userStoragesService
*/
public function __construct(UserStoragesService $userStoragesService) {
$this->userStoragesService = $userStoragesService;
}

/**
* Get all mountpoints applicable for the user
*
@@ -49,7 +58,8 @@ class ConfigAdapter implements IMountProvider {
}
$mountOptions = isset($options['mountOptions']) ? $options['mountOptions'] : [];
if (isset($options['personal']) && $options['personal']) {
$mounts[] = new PersonalMount($options['class'], $mountPoint, $options['options'], $loader, $mountOptions);
$mount = new PersonalMount($this->userStoragesService, $options['id'], $options['class'], $mountPoint, $options['options'], $loader, $mountOptions);
$mounts[] = $mount;
} else {
$mounts[] = new MountPoint($options['class'], $mountPoint, $options['options'], $loader, $mountOptions);
}

+ 179
- 0
apps/files_external/lib/definitionparameter.php View File

@@ -0,0 +1,179 @@
<?php
/**
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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/>
*
*/

namespace OCA\Files_External\Lib;

/**
* Parameter for an external storage definition
*/
class DefinitionParameter implements \JsonSerializable {

/** Value constants */
const VALUE_TEXT = 0;
const VALUE_BOOLEAN = 1;
const VALUE_PASSWORD = 2;
const VALUE_HIDDEN = 3;

/** Flag constants */
const FLAG_NONE = 0;
const FLAG_OPTIONAL = 1;

/** @var string name of parameter */
private $name;

/** @var string human-readable parameter text */
private $text;

/** @var int value type, see self::VALUE_* constants */
private $type = self::VALUE_TEXT;

/** @var int flags, see self::FLAG_* constants */
private $flags = self::FLAG_NONE;

/**
* @param string $name
* @param string $text
*/
public function __construct($name, $text) {
$this->name = $name;
$this->text = $text;
}

/**
* @return string
*/
public function getName() {
return $this->name;
}

/**
* @return string
*/
public function getText() {
return $this->text;
}

/**
* Get value type
*
* @return int
*/
public function getType() {
return $this->type;
}

/**
* Set value type
*
* @param int $type
* @return self
*/
public function setType($type) {
$this->type = $type;
return $this;
}

/**
* @return int
*/
public function getFlags() {
return $this->flags;
}

/**
* @param int $flags
* @return self
*/
public function setFlags($flags) {
$this->flags = $flags;
return $this;
}

/**
* @param int $flag
* @return self
*/
public function setFlag($flag) {
$this->flags |= $flag;
return $this;
}

/**
* @param int $flag
* @return bool
*/
public function isFlagSet($flag) {
return (bool) $this->flags & $flag;
}

/**
* Serialize into JSON for client-side JS
*
* @return string
*/
public function jsonSerialize() {
$prefix = '';
switch ($this->getType()) {
case self::VALUE_BOOLEAN:
$prefix = '!';
break;
case self::VALUE_PASSWORD:
$prefix = '*';
break;
case self::VALUE_HIDDEN:
$prefix = '#';
break;
}

switch ($this->getFlags()) {
case self::FLAG_OPTIONAL:
$prefix = '&' . $prefix;
break;
}

return $prefix . $this->getText();
}

/**
* Validate a parameter value against this
*
* @param mixed $value Value to check
* @return bool success
*/
public function validateValue($value) {
if ($this->getFlags() & self::FLAG_OPTIONAL) {
return true;
}
switch ($this->getType()) {
case self::VALUE_BOOLEAN:
if (!is_bool($value)) {
return false;
}
break;
default:
if (empty($value)) {
return false;
}
break;
}
return true;
}
}

+ 86
- 0
apps/files_external/lib/dependencytrait.php View File

@@ -0,0 +1,86 @@
<?php
/**
* @author Robin McCorkell <rmccorkell@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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/>
*
*/

namespace OCA\Files_External\Lib;

use \OCA\Files_External\Lib\MissingDependency;

/**
* Trait for objects that have dependencies for use
*/
trait DependencyTrait {

/** @var callable|null dependency check */
private $dependencyCheck = null;

/**
* @return bool
*/
public function hasDependencies() {
return !is_null($this->dependencyCheck);
}

/**
* @param callable $dependencyCheck
* @return self
*/
public function setDependencyCheck(callable $dependencyCheck) {
$this->dependencyCheck = $dependencyCheck;
return $this;
}

/**
* Check if object is valid for use
*
* @return MissingDependency[] Unsatisfied dependencies
*/
public function checkDependencies() {
$ret = [];

if ($this->hasDependencies()) {
$result = call_user_func($this->dependencyCheck);
if ($result !== true) {
if (!is_array($result)) {
$result = [$result];
}
foreach ($result as $key => $value) {
if (!($value instanceof MissingDependency)) {
$module = null;
$message = null;
if (is_numeric($key)) {
$module = $value;
} else {
$module = $key;
$message = $value;
}
$value = new MissingDependency($module, $this);
$value->setMessage($message);
}
$ret[] = $value;
}
}
}

return $ret;
}

}


+ 147
- 0
apps/files_external/lib/frontenddefinitiontrait.php View File

@@ -0,0 +1,147 @@
<?php
/**
* @author Robin McCorkell <rmccorkell@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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/>
*
*/

namespace OCA\Files_External\Lib;

use \OCA\Files_External\Lib\DefinitionParameter;
use \OCA\Files_External\Lib\StorageConfig;

/**
* Trait for objects that have a frontend representation
*/
trait FrontendDefinitionTrait {

/** @var string human-readable mechanism name */
private $text;

/** @var DefinitionParameter[] parameters for mechanism */
private $parameters = [];

/** @var string|null custom JS */
private $customJs = null;

/**
* @return string
*/
public function getText() {
return $this->text;
}

/**
* @param string $text
* @return self
*/
public function setText($text) {
$this->text = $text;
return $this;
}

/**
* @param FrontendDefinitionTrait $a
* @param FrontendDefinitionTrait $b
* @return int
*/
public static function lexicalCompare(FrontendDefinitionTrait $a, FrontendDefinitionTrait $b) {
return strcmp($a->getText(), $b->getText());
}

/**
* @return DefinitionParameter[]
*/
public function getParameters() {
return $this->parameters;
}

/**
* @param DefinitionParameter[] $parameters
* @return self
*/
public function addParameters(array $parameters) {
foreach ($parameters as $parameter) {
$this->addParameter($parameter);
}
return $this;
}

/**
* @param DefinitionParameter $parameter
* @return self
*/
public function addParameter(DefinitionParameter $parameter) {
$this->parameters[$parameter->getName()] = $parameter;
return $this;
}

/**
* @return string|null
*/
public function getCustomJs() {
return $this->customJs;
}

/**
* @param string $custom
* @return self
*/
public function setCustomJs($custom) {
$this->customJs = $custom;
return $this;
}

/**
* Serialize into JSON for client-side JS
*
* @return array
*/
public function jsonSerializeDefinition() {
$configuration = [];
foreach ($this->getParameters() as $parameter) {
$configuration[$parameter->getName()] = $parameter;
}

$data = [
'name' => $this->getText(),
'configuration' => $configuration,
];
if (isset($this->customJs)) {
$data['custom'] = $this->getCustomJs();
}
return $data;
}

/**
* Check if parameters are satisfied in a StorageConfig
*
* @param StorageConfig $storage
* @return bool
*/
public function validateStorageDefinition(StorageConfig $storage) {
$options = $storage->getBackendOptions();
foreach ($this->getParameters() as $name => $parameter) {
$value = isset($options[$name]) ? $options[$name] : null;
if (!$parameter->validateValue($value)) {
return false;
}
}
return true;
}

}

+ 64
- 0
apps/files_external/lib/missingdependency.php View File

@@ -0,0 +1,64 @@
<?php
/**
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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/>
*
*/

namespace OCA\Files_External\Lib;

/**
* External storage backend dependency
*/
class MissingDependency {

/** @var string */
private $dependency;

/** @var string|null Custom message */
private $message = null;

/**
* @param string $dependency
*/
public function __construct($dependency) {
$this->dependency = $dependency;
}

/**
* @return string
*/
public function getDependency() {
return $this->dependency;
}

/**
* @return string|null
*/
public function getMessage() {
return $this->message;
}

/**
* @param string $message
* @return self
*/
public function setMessage($message) {
$this->message = $message;
return $this;
}
}

+ 39
- 6
apps/files_external/lib/personalmount.php View File

@@ -20,15 +20,45 @@
*
*/

namespace OCA\Files_External;
namespace OCA\Files_External\Lib;

use OC\Files\Mount\MountPoint;
use OC\Files\Mount\MoveableMount;
use OCA\Files_External\Service\UserStoragesService;

/**
* Person mount points can be moved by the user
*/
class PersonalMount extends MountPoint implements MoveableMount {
/** @var UserStoragesService */
protected $storagesService;

/** @var int */
protected $storageId;

/**
* @param UserStoragesService $storagesService
* @param int $storageId
* @param string|\OC\Files\Storage\Storage $storage
* @param string $mountpoint
* @param array $arguments (optional) configuration for the storage backend
* @param \OCP\Files\Storage\IStorageFactory $loader
* @param array $mountOptions mount specific options
*/
public function __construct(
UserStoragesService $storagesService,
$storageId,
$storage,
$mountpoint,
$arguments = null,
$loader = null,
$mountOptions = null
) {
parent::__construct($storage, $mountpoint, $arguments, $loader, $mountOptions);
$this->storagesService = $storagesService;
$this->storageId = $storageId;
}

/**
* Move the mount point to $target
*
@@ -36,9 +66,13 @@ class PersonalMount extends MountPoint implements MoveableMount {
* @return bool
*/
public function moveMount($target) {
$result = \OC_Mount_Config::movePersonalMountPoint($this->getMountPoint(), $target, \OC_Mount_Config::MOUNT_TYPE_USER);
$storage = $this->storagesService->getStorage($this->storageId);
// remove "/$user/files" prefix
$targetParts = explode('/', trim($target, '/'), 3);
$storage->setMountPoint($targetParts[2]);
$this->storagesService->updateStorage($storage);
$this->setMountPoint($target);
return $result;
return true;
}

/**
@@ -47,8 +81,7 @@ class PersonalMount extends MountPoint implements MoveableMount {
* @return bool
*/
public function removeMount() {
$user = \OCP\User::getUser();
$relativeMountPoint = substr($this->getMountPoint(), strlen('/' . $user . '/files/'));
return \OC_Mount_Config::removeMountPoint($relativeMountPoint, \OC_Mount_Config::MOUNT_TYPE_USER, $user , true);
$this->storagesService->removeStorage($this->storageId);
return true;
}
}

+ 60
- 0
apps/files_external/lib/prioritytrait.php View File

@@ -0,0 +1,60 @@
<?php
/**
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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/>
*
*/

namespace OCA\Files_External\Lib;

use \OCA\Files_External\Service\BackendService;

/**
* Trait to implement priority mechanics for a configuration class
*/
trait PriorityTrait {

/** @var int initial priority */
protected $priority = BackendService::PRIORITY_DEFAULT;

/**
* @return int
*/
public function getPriority() {
return $this->priority;
}

/**
* @param int $priority
* @return self
*/
public function setPriority($priority) {
$this->priority = $priority;
return $this;
}

/**
* @param PriorityTrait $a
* @param PriorityTrait $b
* @return int
*/
public static function priorityCompare(PriorityTrait $a, PriorityTrait $b) {
return ($a->getPriority() - $b->getPriority());
}

}


+ 31
- 14
apps/files_external/lib/storageconfig.php View File

@@ -21,6 +21,8 @@

namespace OCA\Files_external\Lib;

use \OCA\Files_External\Lib\Backend\Backend;

/**
* External storage configuration
*/
@@ -34,11 +36,11 @@ class StorageConfig implements \JsonSerializable {
private $id;

/**
* Backend class name
* Backend
*
* @var string
* @var Backend
*/
private $backendClass;
private $backend;

/**
* Backend options
@@ -138,21 +140,17 @@ class StorageConfig implements \JsonSerializable {
}

/**
* Returns the external storage backend class name
*
* @return string external storage backend class name
* @return Backend
*/
public function getBackendClass() {
return $this->backendClass;
public function getBackend() {
return $this->backend;
}

/**
* Sets the external storage backend class name
*
* @param string external storage backend class name
* @param Backend
*/
public function setBackendClass($backendClass) {
$this->backendClass = $backendClass;
public function setBackend(Backend $backend) {
$this->backend= $backend;
}

/**
@@ -173,6 +171,25 @@ class StorageConfig implements \JsonSerializable {
$this->backendOptions = $backendOptions;
}

/**
* @param string $key
* @return mixed
*/
public function getBackendOption($key) {
if (isset($this->backendOptions[$key])) {
return $this->backendOptions[$key];
}
return null;
}

/**
* @param string $key
* @param mixed $value
*/
public function setBackendOption($key, $value) {
$this->backendOptions[$key] = $value;
}

/**
* Returns the mount priority
*
@@ -283,7 +300,7 @@ class StorageConfig implements \JsonSerializable {
$result['id'] = $this->id;
}
$result['mountPoint'] = $this->mountPoint;
$result['backendClass'] = $this->backendClass;
$result['backendClass'] = $this->backend->getClass();
$result['backendOptions'] = $this->backendOptions;
if (!is_null($this->priority)) {
$result['priority'] = $this->priority;

+ 51
- 0
apps/files_external/lib/storagemodifiertrait.php View File

@@ -0,0 +1,51 @@
<?php
/**
* @author Robin McCorkell <rmccorkell@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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/>
*
*/

namespace OCA\Files_External\Lib;

use \OCP\Files\Storage;
use \OCA\Files_External\Lib\StorageConfig;

/**
* Trait for objects that can modify StorageConfigs and wrap Storages
*/
trait StorageModifierTrait {

/**
* Modify a StorageConfig parameters
*
* @param StorageConfig $storage
*/
public function manipulateStorageConfig(StorageConfig &$storage) {
}

/**
* Wrap a Storage if necessary
*
* @param Storage $storage
* @return Storage
*/
public function wrapStorage(Storage $storage) {
return $storage;
}

}


+ 129
- 0
apps/files_external/lib/visibilitytrait.php View File

@@ -0,0 +1,129 @@
<?php
/**
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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/>
*
*/

namespace OCA\Files_External\Lib;

use \OCA\Files_External\Service\BackendService;

/**
* Trait to implement visibility mechanics for a configuration class
*/
trait VisibilityTrait {

/** @var int visibility */
protected $visibility = BackendService::VISIBILITY_DEFAULT;

/** @var int allowed visibilities */
protected $allowedVisibility = BackendService::VISIBILITY_DEFAULT;

/**
* @return int
*/
public function getVisibility() {
return $this->visibility;
}

/**
* Check if the backend is visible for a user type
*
* @param int $visibility
* @return bool
*/
public function isVisibleFor($visibility) {
if ($this->visibility & $visibility) {
return true;
}
return false;
}

/**
* @param int $visibility
* @return self
*/
public function setVisibility($visibility) {
$this->visibility = $visibility;
$this->allowedVisibility |= $visibility;
return $this;
}

/**
* @param int $visibility
* @return self
*/
public function addVisibility($visibility) {
return $this->setVisibility($this->visibility | $visibility);
}

/**
* @param int $visibility
* @return self
*/
public function removeVisibility($visibility) {
return $this->setVisibility($this->visibility & ~$visibility);
}

/**
* @return int
*/
public function getAllowedVisibility() {
return $this->allowedVisibility;
}

/**
* Check if the backend is allowed to be visible for a user type
*
* @param int $allowedVisibility
* @return bool
*/
public function isAllowedVisibleFor($allowedVisibility) {
if ($this->allowedVisibility & $allowedVisibility) {
return true;
}
return false;
}

/**
* @param int $allowedVisibility
* @return self
*/
public function setAllowedVisibility($allowedVisibility) {
$this->allowedVisibility = $allowedVisibility;
$this->visibility &= $allowedVisibility;
return $this;
}

/**
* @param int $allowedVisibility
* @return self
*/
public function addAllowedVisibility($allowedVisibility) {
return $this->setAllowedVisibility($this->allowedVisibility | $allowedVisibility);
}

/**
* @param int $allowedVisibility
* @return self
*/
public function removeAllowedVisibility($allowedVisibility) {
return $this->setAllowedVisibility($this->allowedVisibility & ~$allowedVisibility);
}

}

+ 10
- 24
apps/files_external/personal.php View File

@@ -24,34 +24,20 @@
*
*/

OCP\Util::addScript('files_external', 'settings');
OCP\Util::addStyle('files_external', 'settings');
$backends = OC_Mount_Config::getPersonalBackends();
use \OCA\Files_External\Service\BackendService;

$mounts = OC_Mount_Config::getPersonalMountPoints();
$hasId = true;
foreach ($mounts as $mount) {
if (!isset($mount['id'])) {
// some mount points are missing ids
$hasId = false;
break;
}
}
$app = new \OCA\Files_external\Appinfo\Application();
$appContainer = $app->getContainer();
$backendService = $appContainer->query('OCA\Files_External\Service\BackendService');
$userStoragesService = $appContainer->query('OCA\Files_external\Service\UserStoragesService');

if (!$hasId) {
$service = new \OCA\Files_external\Service\UserStoragesService(\OC::$server->getUserSession());
// this will trigger the new storage code which will automatically
// generate storage config ids
$service->getAllStorages();
// re-read updated config
$mounts = OC_Mount_Config::getPersonalMountPoints();
// TODO: use the new storage config format in the template
}
OCP\Util::addScript('files_external', 'settings');
OCP\Util::addStyle('files_external', 'settings');

$tmpl = new OCP\Template('files_external', 'settings');
$tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled());
$tmpl->assign('isAdminPage', false);
$tmpl->assign('mounts', $mounts);
$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies());
$tmpl->assign('backends', $backends);
$tmpl->assign('storages', $userStoragesService->getAllStorages());
$tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends()));
$tmpl->assign('backends', $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_PERSONAL));
return $tmpl->fetchPage();

+ 170
- 0
apps/files_external/service/backendservice.php View File

@@ -0,0 +1,170 @@
<?php
/**
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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/>
*
*/

namespace OCA\Files_External\Service;

use \OCP\IConfig;

use \OCA\Files_External\Lib\Backend\Backend;

/**
* Service class to manage backend definitions
*/
class BackendService {

/** Visibility constants for VisibilityTrait */
const VISIBILITY_NONE = 0;
const VISIBILITY_PERSONAL = 1;
const VISIBILITY_ADMIN = 2;
//const VISIBILITY_ALIENS = 4;

const VISIBILITY_DEFAULT = 3; // PERSONAL | ADMIN

/** Priority constants for PriorityTrait */
const PRIORITY_DEFAULT = 100;

/** @var IConfig */
protected $config;

/** @var bool */
private $userMountingAllowed = true;

/** @var string[] */
private $userMountingBackends = [];

/** @var Backend[] */
private $backends = [];

/**
* @param IConfig $config
*/
public function __construct(
IConfig $config
) {
$this->config = $config;

// Load config values
if ($this->config->getAppValue('files_external', 'allow_user_mounting', 'yes') !== 'yes') {
$this->userMountingAllowed = false;
}
$this->userMountingBackends = explode(',',
$this->config->getAppValue('files_external', 'user_mounting_backends', '')
);
}

/**
* Register a backend
*
* @param Backend $backend
*/
public function registerBackend(Backend $backend) {
if (!$this->isAllowedUserBackend($backend)) {
$backend->removeVisibility(BackendService::VISIBILITY_PERSONAL);
}
$this->backends[$backend->getClass()] = $backend;
}

/**
* @param Backend[] $backends
*/
public function registerBackends(array $backends) {
foreach ($backends as $backend) {
$this->registerBackend($backend);
}
}

/**
* Get all backends
*
* @return Backend[]
*/
public function getBackends() {
return $this->backends;
}

/**
* Get all available backends
*
* @return Backend[]
*/
public function getAvailableBackends() {
return array_filter($this->getBackends(), function($backend) {
return empty($backend->checkDependencies());
});
}

/**
* Get backends visible for $visibleFor
*
* @param int $visibleFor
* @return Backend[]
*/
public function getBackendsVisibleFor($visibleFor) {
return array_filter($this->getAvailableBackends(), function($backend) use ($visibleFor) {
return $backend->isVisibleFor($visibleFor);
});
}

/**
* Get backends allowed to be visible for $visibleFor
*
* @param int $visibleFor
* @return Backend[]
*/
public function getBackendsAllowedVisibleFor($visibleFor) {
return array_filter($this->getAvailableBackends(), function($backend) use ($visibleFor) {
return $backend->isAllowedVisibleFor($visibleFor);
});
}

/**
* @param string $class Backend class name
* @return Backend|null
*/
public function getBackend($class) {
if (isset($this->backends[$class])) {
return $this->backends[$class];
}
return null;
}

/**
* @return bool
*/
public function isUserMountingAllowed() {
return $this->userMountingAllowed;
}

/**
* Check a backend if a user is allowed to mount it
*
* @param Backend $backend
* @return bool
*/
protected function isAllowedUserBackend(Backend $backend) {
if ($this->userMountingAllowed &&
in_array($backend->getClass(), $this->userMountingBackends)
) {
return true;
}
return false;
}
}

+ 64
- 4
apps/files_external/service/storagesservice.php View File

@@ -28,12 +28,23 @@ use \OC\Files\Filesystem;

use \OCA\Files_external\Lib\StorageConfig;
use \OCA\Files_external\NotFoundException;
use \OCA\Files_External\Service\BackendService;

/**
* Service class to manage external storages
*/
abstract class StoragesService {

/** @var BackendService */
protected $backendService;

/**
* @param BackendService $backendService
*/
public function __construct(BackendService $backendService) {
$this->backendService = $backendService;
}

/**
* Read legacy config data
*
@@ -60,14 +71,17 @@ abstract class StoragesService {
$applicable,
$storageOptions
) {
$storageConfig->setBackendClass($storageOptions['class']);
$backend = $this->backendService->getBackend($storageOptions['class']);
$storageConfig->setBackend($backend);

$storageConfig->setBackendOptions($storageOptions['options']);
if (isset($storageOptions['mountOptions'])) {
$storageConfig->setMountOptions($storageOptions['mountOptions']);
}
if (isset($storageOptions['priority'])) {
$storageConfig->setPriority($storageOptions['priority']);
if (!isset($storageOptions['priority'])) {
$storageOptions['priority'] = $backend->getPriority();
}
$storageConfig->setPriority($storageOptions['priority']);

if ($mountType === \OC_Mount_Config::MOUNT_TYPE_USER) {
$applicableUsers = $storageConfig->getApplicableUsers();
@@ -222,7 +236,7 @@ abstract class StoragesService {

$options = [
'id' => $storageConfig->getId(),
'class' => $storageConfig->getBackendClass(),
'class' => $storageConfig->getBackend()->getClass(),
'options' => $storageConfig->getBackendOptions(),
];

@@ -296,6 +310,52 @@ abstract class StoragesService {
return $newStorage;
}

/**
* Create a storage from its parameters
*
* @param string $mountPoint storage mount point
* @param string $backendClass backend class name
* @param array $backendOptions backend-specific options
* @param array|null $mountOptions mount-specific options
* @param array|null $applicableUsers users for which to mount the storage
* @param array|null $applicableGroups groups for which to mount the storage
* @param int|null $priority priority
*
* @return StorageConfig
*/
public function createStorage(
$mountPoint,
$backendClass,
$backendOptions,
$mountOptions = null,
$applicableUsers = null,
$applicableGroups = null,
$priority = null
) {
$backend = $this->backendService->getBackend($backendClass);
if (!$backend) {
throw new \InvalidArgumentException('Unable to get backend for backend class '.$backendClass);
}
$newStorage = new StorageConfig();
$newStorage->setMountPoint($mountPoint);
$newStorage->setBackend($backend);
$newStorage->setBackendOptions($backendOptions);
if (isset($mountOptions)) {
$newStorage->setMountOptions($mountOptions);
}
if (isset($applicableUsers)) {
$newStorage->setApplicableUsers($applicableUsers);
}
if (isset($applicableGroups)) {
$newStorage->setApplicableGroups($applicableGroups);
}
if (isset($priority)) {
$newStorage->setPriority($priority);
}

return $newStorage;
}

/**
* Triggers the given hook signal for all the applicables given
*

+ 4
- 0
apps/files_external/service/userstoragesservice.php View File

@@ -26,6 +26,7 @@ use \OC\Files\Filesystem;

use \OCA\Files_external\Lib\StorageConfig;
use \OCA\Files_external\NotFoundException;
use \OCA\Files_External\Service\BackendService;

/**
* Service class to manage user external storages
@@ -43,12 +44,15 @@ class UserStoragesService extends StoragesService {
/**
* Create a user storages service
*
* @param BackendService $backendService
* @param IUserSession $userSession user session
*/
public function __construct(
BackendService $backendService,
IUserSession $userSession
) {
$this->userSession = $userSession;
parent::__construct($backendService);
}

/**

+ 12
- 39
apps/files_external/settings.php View File

@@ -26,54 +26,27 @@
*
*/

use \OCA\Files_External\Service\BackendService;

OC_Util::checkAdminUser();

$app = new \OCA\Files_external\Appinfo\Application();
$appContainer = $app->getContainer();
$backendService = $appContainer->query('OCA\Files_External\Service\BackendService');
$globalStoragesService = $appContainer->query('OCA\Files_external\Service\GlobalStoragesService');

OCP\Util::addScript('files_external', 'settings');
OCP\Util::addStyle('files_external', 'settings');

\OC_Util::addVendorScript('select2/select2');
\OC_Util::addVendorStyle('select2/select2');

$backends = OC_Mount_Config::getBackends();
$personal_backends = array();
$enabled_backends = explode(',', OCP\Config::getAppValue('files_external', 'user_mounting_backends', ''));
foreach ($backends as $class => $backend)
{
if ($class != '\OC\Files\Storage\Local')
{
$personal_backends[$class] = array(
'backend' => $backend['backend'],
'enabled' => in_array($class, $enabled_backends),
);
}
}

$mounts = OC_Mount_Config::getSystemMountPoints();
$hasId = true;
foreach ($mounts as $mount) {
if (!isset($mount['id'])) {
// some mount points are missing ids
$hasId = false;
break;
}
}

if (!$hasId) {
$service = new \OCA\Files_external\Service\GlobalStoragesService();
// this will trigger the new storage code which will automatically
// generate storage config ids
$service->getAllStorages();
// re-read updated config
$mounts = OC_Mount_Config::getSystemMountPoints();
// TODO: use the new storage config format in the template
}

$tmpl = new OCP\Template('files_external', 'settings');
$tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled());
$tmpl->assign('isAdminPage', true);
$tmpl->assign('mounts', $mounts);
$tmpl->assign('backends', $backends);
$tmpl->assign('personal_backends', $personal_backends);
$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies());
$tmpl->assign('allowUserMounting', OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes'));
$tmpl->assign('storages', $globalStoragesService->getAllStorages());
$tmpl->assign('backends', $backendService->getBackendsVisibleFor(BackendService::VISIBILITY_ADMIN));
$tmpl->assign('userBackends', $backendService->getBackendsAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL));
$tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends()));
$tmpl->assign('allowUserMounting', $backendService->isUserMountingAllowed());
return $tmpl->fetchPage();

+ 133
- 82
apps/files_external/templates/settings.php View File

@@ -1,3 +1,8 @@
<?php
use \OCA\Files_External\Lib\Backend\Backend;
use \OCA\Files_External\Lib\DefinitionParameter;
use \OCA\Files_External\Service\BackendService;
?>
<form id="files_external" class="section" data-encryption-enabled="<?php echo $_['encryptionEnabled']?'true': 'false'; ?>">
<h2><?php p($l->t('External Storage')); ?></h2>
<?php if (isset($_['dependencies']) and ($_['dependencies']<>'')) print_unescaped(''.$_['dependencies'].''); ?>
@@ -14,103 +19,149 @@
</tr>
</thead>
<tbody>
<?php $_['mounts'] = array_merge($_['mounts'], array('' => array('id' => ''))); ?>
<?php foreach ($_['mounts'] as $mount): ?>
<tr <?php print_unescaped(isset($mount['mountpoint']) ? 'class="'.OC_Util::sanitizeHTML($mount['class']).'"' : 'id="addMountPoint"'); ?> data-id="<?php p($mount['id']) ?>">
<?php foreach ($_['storages'] as $storage): ?>
<tr class="<?php p($storage->getBackend()->getClass()); ?>" data-id="<?php p($storage->getId()); ?>">
<td class="status">
<span></span>
</td>
<td class="mountPoint"><input type="text" name="mountPoint"
value="<?php p(isset($mount['mountpoint']) ? $mount['mountpoint'] : ''); ?>"
data-mountpoint="<?php p(isset($mount['mountpoint']) ? $mount['mountpoint'] : ''); ?>"
value="<?php p(ltrim($storage->getMountPoint(), '/')); ?>"
data-mountpoint="<?php p(ltrim($storage->getMountPoint(), '/')); ?>"
placeholder="<?php p($l->t('Folder name')); ?>" />
</td>
<?php if (!isset($mount['mountpoint'])): ?>
<td class="backend">
<select id="selectBackend" class="selectBackend" data-configurations='<?php p(json_encode($_['backends'])); ?>'>
<option value="" disabled selected
style="display:none;"><?php p($l->t('Add storage')); ?></option>
<?php foreach ($_['backends'] as $class => $backend): ?>
<option value="<?php p($class); ?>"><?php p($backend['backend']); ?></option>
<?php endforeach; ?>
</select>
</td>
<?php else: ?>
<td class="backend" data-class="<?php p($mount['class']); ?>"><?php p($mount['backend']); ?>
</td>
<?php endif; ?>
<td class ="configuration">
<?php if (isset($mount['options'])): ?>
<?php foreach ($mount['options'] as $parameter => $value): ?>
<?php if (isset($_['backends'][$mount['class']]['configuration'][$parameter])): ?>
<td class="backend" data-class="<?php p($storage->getBackend()->getClass()); ?>"><?php p($storage->getBackend()->getText()); ?>
</td>
<td class="configuration">
<?php $options = $storage->getBackendOptions(); ?>
<?php foreach ($storage->getBackend()->getParameters() as $parameter): ?>
<?php
$value = '';
if (isset($options[$parameter->getName()])) {
$value = $options[$parameter->getName()];
}
$placeholder = $parameter->getText();
$is_optional = $parameter->isFlagSet(DefinitionParameter::FLAG_OPTIONAL);

switch ($parameter->getType()) {
case DefinitionParameter::VALUE_PASSWORD: ?>
<input type="password"
<?php if ($is_optional): ?> class="optional"<?php endif; ?>
data-parameter="<?php p($parameter->getName()); ?>"
value="<?php p($value); ?>"
placeholder="<?php p($placeholder); ?>"
/>
<?php
$placeholder = $_['backends'][$mount['class']]['configuration'][$parameter];
$is_optional = FALSE;
if (strpos($placeholder, '&') === 0) {
$is_optional = TRUE;
$placeholder = substr($placeholder, 1);
}
?>
<?php if (strpos($placeholder, '*') === 0): ?>
<input type="password"
<?php if ($is_optional): ?> class="optional"<?php endif; ?>
data-parameter="<?php p($parameter); ?>"
value="<?php p($value); ?>"
placeholder="<?php p(substr($placeholder, 1)); ?>" />
<?php elseif (strpos($placeholder, '!') === 0): ?>
<label><input type="checkbox"
data-parameter="<?php p($parameter); ?>"
<?php if ($value == 'true'): ?> checked="checked"<?php endif; ?>
/><?php p(substr($placeholder, 1)); ?></label>
<?php elseif (strpos($placeholder, '#') === 0): ?>
<input type="hidden"
data-parameter="<?php p($parameter); ?>"
value="<?php p($value); ?>" />
<?php else: ?>
<input type="text"
<?php if ($is_optional): ?> class="optional"<?php endif; ?>
data-parameter="<?php p($parameter); ?>"
value="<?php p($value); ?>"
placeholder="<?php p($placeholder); ?>" />
<?php endif; ?>
<?php endif; ?>
<?php endforeach; ?>
<?php if (isset($_['backends'][$mount['class']]['custom'])): ?>
<?php OCP\Util::addScript('files_external', $_['backends'][$mount['class']]['custom']); ?>
<?php endif; ?>
<?php endif; ?>
break;
case DefinitionParameter::VALUE_BOOLEAN: ?>
<label>
<input type="checkbox"
data-parameter="<?php p($parameter->getName()); ?>"
<?php if ($value == 'true'): ?> checked="checked"<?php endif; ?>
/>
<?php p($placeholder); ?>
</label>
<?php
break;
case DefinitionParameter::VALUE_HIDDEN: ?>
<input type="hidden"
data-parameter="<?php p($parameter->getName()); ?>"
value="<?php p($value); ?>"
/>
<?php
break;
default: ?>
<input type="text"
<?php if ($is_optional): ?> class="optional"<?php endif; ?>
data-parameter="<?php p($parameter->getName()); ?>"
value="<?php p($value); ?>"
placeholder="<?php p($placeholder); ?>"
/>
<?php
}
?>
<?php endforeach; ?>
<?php
$customJs = $storage->getBackend()->getCustomJs();
if (isset($customJs)) {
\OCP\Util::addScript('files_external', $customJs);
}
?>
</td>
<?php if ($_['isAdminPage']): ?>
<td class="applicable"
align="right"
data-applicable-groups='<?php if (isset($mount['applicable']['groups']))
print_unescaped(json_encode($mount['applicable']['groups'])); ?>'
data-applicable-users='<?php if (isset($mount['applicable']['users']))
print_unescaped(json_encode($mount['applicable']['users'])); ?>'>
<input type="hidden" class="applicableUsers" style="width:20em;" value=""/>
</td>
<td class="applicable"
align="right"
data-applicable-groups='<?php print_unescaped(json_encode($storage->getApplicableGroups())); ?>'
data-applicable-users='<?php print_unescaped(json_encode($storage->getApplicableUsers())); ?>'>
<input type="hidden" class="applicableUsers" style="width:20em;" value=""/>
</td>
<?php endif; ?>
<td class="mountOptionsToggle <?php if (!isset($mount['mountpoint'])) { p('hidden'); } ?>"
><img
<td class="mountOptionsToggle">
<img
class="svg action"
title="<?php p($l->t('Advanced settings')); ?>"
alt="<?php p($l->t('Advanced settings')); ?>"
src="<?php print_unescaped(image_path('core', 'actions/settings.svg')); ?>" />
<input type="hidden" class="mountOptions" value="<?php isset($mount['mountOptions']) ? p(json_encode($mount['mountOptions'])) : '' ?>" />
src="<?php print_unescaped(image_path('core', 'actions/settings.svg')); ?>"
/>
<input type="hidden" class="mountOptions" value="<?php p(json_encode($storage->getMountOptions())); ?>" />
<?php if ($_['isAdminPage']): ?>
<?php if (isset($mount['priority'])): ?>
<input type="hidden" class="priority" value="<?php p($mount['priority']) ?>" />
<?php endif; ?>
<input type="hidden" class="priority" value="<?php p($storage->getPriority()); ?>" />
<?php endif; ?>
</td>
<td <?php if (isset($mount['mountpoint'])): ?>class="remove"
<?php else: ?>class="hidden"
<?php endif ?>><img alt="<?php p($l->t('Delete')); ?>"
title="<?php p($l->t('Delete')); ?>"
class="svg action"
src="<?php print_unescaped(image_path('core', 'actions/delete.svg')); ?>" /></td>
<td class="remove">
<img alt="<?php p($l->t('Delete')); ?>"
title="<?php p($l->t('Delete')); ?>"
class="svg action"
src="<?php print_unescaped(image_path('core', 'actions/delete.svg')); ?>"
/>
</td>
</tr>
<?php endforeach; ?>
<tr id="addMountPoint">
<td class="status">
<span></span>
</td>
<td class="mountPoint"><input type="text" name="mountPoint" value=""
placeholder="<?php p($l->t('Folder name')); ?>">
</td>
<td class="backend">
<select id="selectBackend" class="selectBackend" data-configurations='<?php p(json_encode($_['backends'])); ?>'>
<option value="" disabled selected
style="display:none;">
<?php p($l->t('Add storage')); ?>
</option>
<?php
$sortedBackends = $_['backends'];
uasort($sortedBackends, function($a, $b) {
return strcasecmp($a->getText(), $b->getText());
});
?>
<?php foreach ($sortedBackends as $backend): ?>
<option value="<?php p($backend->getClass()); ?>"><?php p($backend->getText()); ?></option>
<?php endforeach; ?>
</select>
</td>
<td class="configuration"</td>
<?php if ($_['isAdminPage']): ?>
<td class="applicable" align="right">
<input type="hidden" class="applicableUsers" style="width:20em;" value="" />
</td>
<?php endif; ?>
<td class="mountOptionsToggle hidden">
<img class="svg action"
title="<?php p($l->t('Advanced settings')); ?>"
alt="<?php p($l->t('Advanced settings')); ?>"
src="<?php print_unescaped(image_path('core', 'actions/settings.svg')); ?>"
/>
<input type="hidden" class="mountOptions" value="" />
</td>
<td class="hidden">
<img class="svg action"
alt="<?php p($l->t('Delete')); ?>"
title="<?php p($l->t('Delete')); ?>"
src="<?php print_unescaped(image_path('core', 'actions/delete.svg')); ?>"
/>
</td>
</tr>
</tbody>
</table>
<br />
@@ -123,9 +174,9 @@

<p id="userMountingBackends"<?php if ($_['allowUserMounting'] != 'yes'): ?> class="hidden"<?php endif; ?>>
<?php p($l->t('Allow users to mount the following external storage')); ?><br />
<?php $i = 0; foreach ($_['personal_backends'] as $class => $backend): ?>
<input type="checkbox" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($class); ?>" <?php if ($backend['enabled']) print_unescaped(' checked="checked"'); ?> />
<label for="allowUserMountingBackends<?php p($i); ?>"><?php p($backend['backend']); ?></label> <br />
<?php $i = 0; foreach ($_['userBackends'] as $backend): ?>
<input type="checkbox" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getClass()); ?>" <?php if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) print_unescaped(' checked="checked"'); ?> />
<label for="allowUserMountingBackends<?php p($i); ?>"><?php p($backend->getText()); ?></label> <br />
<?php $i++; ?>
<?php endforeach; ?>
</p>

+ 3
- 1
apps/files_external/tests/controller/globalstoragescontrollertest.php View File

@@ -28,7 +28,9 @@ use \OCA\Files_external\NotFoundException;
class GlobalStoragesControllerTest extends StoragesControllerTest {
public function setUp() {
parent::setUp();
$this->service = $this->getMock('\OCA\Files_external\Service\GlobalStoragesService');
$this->service = $this->getMockBuilder('\OCA\Files_external\Service\GlobalStoragesService')
->disableOriginalConstructor()
->getMock();

$this->controller = new GlobalStoragesController(
'files_external',

+ 60
- 1
apps/files_external/tests/controller/storagescontrollertest.php View File

@@ -47,10 +47,32 @@ abstract class StoragesControllerTest extends \Test\TestCase {
\OC_Mount_Config::$skipTest = false;
}

protected function getBackendMock($class = '\OCA\Files_External\Lib\Backend\SMB', $storageClass = '\OC\Files\Storage\SMB') {
$backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend')
->disableOriginalConstructor()
->getMock();
$backend->method('getStorageClass')
->willReturn($storageClass);
$backend->method('getClass')
->willReturn($storageClass);
return $backend;
}

public function testAddStorage() {
$backend = $this->getBackendMock();
$backend->method('validateStorage')
->willReturn(true);
$backend->method('isVisibleFor')
->willReturn(true);

$storageConfig = new StorageConfig(1);
$storageConfig->setMountPoint('mount');
$storageConfig->setBackend($backend);
$storageConfig->setBackendOptions([]);

$this->service->expects($this->once())
->method('createStorage')
->will($this->returnValue($storageConfig));
$this->service->expects($this->once())
->method('addStorage')
->will($this->returnValue($storageConfig));
@@ -71,9 +93,20 @@ abstract class StoragesControllerTest extends \Test\TestCase {
}

public function testUpdateStorage() {
$backend = $this->getBackendMock();
$backend->method('validateStorage')
->willReturn(true);
$backend->method('isVisibleFor')
->willReturn(true);

$storageConfig = new StorageConfig(1);
$storageConfig->setMountPoint('mount');
$storageConfig->setBackend($backend);
$storageConfig->setBackendOptions([]);

$this->service->expects($this->once())
->method('createStorage')
->will($this->returnValue($storageConfig));
$this->service->expects($this->once())
->method('updateStorage')
->will($this->returnValue($storageConfig));
@@ -106,6 +139,14 @@ abstract class StoragesControllerTest extends \Test\TestCase {
* @dataProvider mountPointNamesProvider
*/
public function testAddOrUpdateStorageInvalidMountPoint($mountPoint) {
$storageConfig = new StorageConfig(1);
$storageConfig->setMountPoint($mountPoint);
$storageConfig->setBackend($this->getBackendMock());
$storageConfig->setBackendOptions([]);

$this->service->expects($this->exactly(2))
->method('createStorage')
->will($this->returnValue($storageConfig));
$this->service->expects($this->never())
->method('addStorage');
$this->service->expects($this->never())
@@ -138,6 +179,9 @@ abstract class StoragesControllerTest extends \Test\TestCase {
}

public function testAddOrUpdateStorageInvalidBackend() {
$this->service->expects($this->exactly(2))
->method('createStorage')
->will($this->throwException(new \InvalidArgumentException()));
$this->service->expects($this->never())
->method('addStorage');
$this->service->expects($this->never())
@@ -170,6 +214,20 @@ abstract class StoragesControllerTest extends \Test\TestCase {
}

public function testUpdateStorageNonExisting() {
$backend = $this->getBackendMock();
$backend->method('validateStorage')
->willReturn(true);
$backend->method('isVisibleFor')
->willReturn(true);

$storageConfig = new StorageConfig(255);
$storageConfig->setMountPoint('mount');
$storageConfig->setBackend($backend);
$storageConfig->setBackendOptions([]);

$this->service->expects($this->once())
->method('createStorage')
->will($this->returnValue($storageConfig));
$this->service->expects($this->once())
->method('updateStorage')
->will($this->throwException(new NotFoundException()));
@@ -206,9 +264,10 @@ abstract class StoragesControllerTest extends \Test\TestCase {
}

public function testGetStorage() {
$backend = $this->getBackendMock();
$storageConfig = new StorageConfig(1);
$storageConfig->setMountPoint('test');
$storageConfig->setBackendClass('\OC\Files\Storage\SMB');
$storageConfig->setBackend($backend);
$storageConfig->setBackendOptions(['user' => 'test', 'password', 'password123']);
$storageConfig->setMountOptions(['priority' => false]);


+ 17
- 34
apps/files_external/tests/controller/userstoragescontrollertest.php View File

@@ -24,6 +24,8 @@ use \OCA\Files_external\Controller\UserStoragesController;
use \OCA\Files_external\Service\UserStoragesService;
use \OCP\AppFramework\Http;
use \OCA\Files_external\NotFoundException;
use \OCA\Files_External\Lib\StorageConfig;
use \OCA\Files_External\Service\BackendService;

class UserStoragesControllerTest extends StoragesControllerTest {

@@ -44,41 +46,22 @@ class UserStoragesControllerTest extends StoragesControllerTest {
$this->getMock('\OCP\IL10N'),
$this->service
);

$config = \OC::$server->getConfig();

$this->oldAllowedBackends = $config->getAppValue(
'files_external',
'user_mounting_backends',
''
);
$config->setAppValue(
'files_external',
'user_mounting_backends',
'\OC\Files\Storage\SMB'
);
}

public function tearDown() {
$config = \OC::$server->getConfig();
$config->setAppValue(
'files_external',
'user_mounting_backends',
$this->oldAllowedBackends
);
parent::tearDown();
}
public function testAddOrUpdateStorageDisallowedBackend() {
$backend = $this->getBackendMock();
$backend->method('isVisibleFor')
->with(BackendService::VISIBILITY_PERSONAL)
->willReturn(false);

function disallowedBackendClassProvider() {
return array(
array('\OC\Files\Storage\Local'),
array('\OC\Files\Storage\FTP'),
);
}
/**
* @dataProvider disallowedBackendClassProvider
*/
public function testAddOrUpdateStorageDisallowedBackend($backendClass) {
$storageConfig = new StorageConfig(1);
$storageConfig->setMountPoint('mount');
$storageConfig->setBackend($backend);
$storageConfig->setBackendOptions([]);

$this->service->expects($this->exactly(2))
->method('createStorage')
->will($this->returnValue($storageConfig));
$this->service->expects($this->never())
->method('addStorage');
$this->service->expects($this->never())
@@ -86,7 +69,7 @@ class UserStoragesControllerTest extends StoragesControllerTest {

$response = $this->controller->create(
'mount',
$backendClass,
'\OC\Files\Storage\SMB',
array(),
[],
[],
@@ -99,7 +82,7 @@ class UserStoragesControllerTest extends StoragesControllerTest {
$response = $this->controller->update(
1,
'mount',
$backendClass,
'\OC\Files\Storage\SMB',
array(),
[],
[],

+ 0
- 104
apps/files_external/tests/dynamicmountconfig.php View File

@@ -1,104 +0,0 @@
<?php
/**
* @author Joas Schilling <nickvergessen@owncloud.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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/>
*
*/

require_once __DIR__ . '/../../../lib/base.php';

/**
* Class Test_Mount_Config_Dummy_Backend
*/
class Test_Mount_Config_Dummy_Backend {
public static $checkDependencies = true;

public static function checkDependencies() {
return self::$checkDependencies;
}
}

/**
* Class Test_Dynamic_Mount_Config
*/
class Test_Dynamic_Mount_Config extends \Test\TestCase {

private $backup;

public function testRegistration() {

// second registration shall return false
$result = OC_Mount_Config::registerBackend('Test_Mount_Config_Dummy_Backend', array(
'backend' => 'Test Dummy',
'configuration' => array(),
'has_dependencies' => true));

$this->assertTrue($result);
}

public function testDependencyGetBackend() {

// is the backend listed?
Test_Mount_Config_Dummy_Backend::$checkDependencies = true;
$backEnds = OC_Mount_Config::getBackends();
$this->assertArrayHasKey('Test_Mount_Config_Dummy_Backend', $backEnds);

// backend shall not be listed
Test_Mount_Config_Dummy_Backend::$checkDependencies = false;

$backEnds = OC_Mount_Config::getBackends();
$this->assertArrayNotHasKey('Test_Mount_Config_Dummy_Backend', $backEnds);

}

public function testCheckDependencies() {

Test_Mount_Config_Dummy_Backend::$checkDependencies = true;
$message = OC_Mount_Config::checkDependencies();
$this->assertEmpty($message);

// backend shall not be listed
Test_Mount_Config_Dummy_Backend::$checkDependencies = array('dummy');

$message = OC_Mount_Config::checkDependencies();
$this->assertEquals('<br /><b>Note:</b> "dummy" is not installed. Mounting of <i>Test Dummy</i> is not possible. Please ask your system administrator to install it.',
$message);

}

protected function setUp() {
parent::setUp();

$this->backup = OC_Mount_Config::setUp();

// register dummy backend
$result = OC_Mount_Config::registerBackend('Test_Mount_Config_Dummy_Backend', array(
'backend' => 'Test Dummy',
'configuration' => array(),
'has_dependencies' => true));

$this->assertTrue($result);
}

protected function tearDown()
{
OC_Mount_Config::setUp($this->backup);
parent::tearDown();
}
}

+ 14
- 12
apps/files_external/tests/service/globalstoragesservicetest.php View File

@@ -29,7 +29,7 @@ use \OCA\Files_external\Lib\StorageConfig;
class GlobalStoragesServiceTest extends StoragesServiceTest {
public function setUp() {
parent::setUp();
$this->service = new GlobalStoragesService();
$this->service = new GlobalStoragesService($this->backendService);
}

public function tearDown() {
@@ -59,7 +59,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest {
return [
// all users
[
$this->makeStorageConfig([
[
'mountPoint' => 'mountpoint',
'backendClass' => '\OC\Files\Storage\SMB',
'backendOptions' => [
@@ -70,11 +70,11 @@ class GlobalStoragesServiceTest extends StoragesServiceTest {
'applicableUsers' => [],
'applicableGroups' => [],
'priority' => 15,
]),
],
],
// some users
[
$this->makeStorageConfig([
[
'mountPoint' => 'mountpoint',
'backendClass' => '\OC\Files\Storage\SMB',
'backendOptions' => [
@@ -85,11 +85,11 @@ class GlobalStoragesServiceTest extends StoragesServiceTest {
'applicableUsers' => ['user1', 'user2'],
'applicableGroups' => [],
'priority' => 15,
]),
],
],
// some groups
[
$this->makeStorageConfig([
[
'mountPoint' => 'mountpoint',
'backendClass' => '\OC\Files\Storage\SMB',
'backendOptions' => [
@@ -100,11 +100,11 @@ class GlobalStoragesServiceTest extends StoragesServiceTest {
'applicableUsers' => [],
'applicableGroups' => ['group1', 'group2'],
'priority' => 15,
]),
],
],
// both users and groups
[
$this->makeStorageConfig([
[
'mountPoint' => 'mountpoint',
'backendClass' => '\OC\Files\Storage\SMB',
'backendOptions' => [
@@ -115,7 +115,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest {
'applicableUsers' => ['user1', 'user2'],
'applicableGroups' => ['group1', 'group2'],
'priority' => 15,
]),
],
],
];
}
@@ -123,7 +123,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest {
/**
* @dataProvider storageDataProvider
*/
public function testAddStorage($storage) {
public function testAddStorage($storageParams) {
$storage = $this->makeStorageConfig($storageParams);
$newStorage = $this->service->addStorage($storage);

$this->assertEquals(1, $newStorage->getId());
@@ -132,7 +133,7 @@ class GlobalStoragesServiceTest extends StoragesServiceTest {
$newStorage = $this->service->getStorage(1);

$this->assertEquals($storage->getMountPoint(), $newStorage->getMountPoint());
$this->assertEquals($storage->getBackendClass(), $newStorage->getBackendClass());
$this->assertEquals($storage->getBackend(), $newStorage->getBackend());
$this->assertEquals($storage->getBackendOptions(), $newStorage->getBackendOptions());
$this->assertEquals($storage->getApplicableUsers(), $newStorage->getApplicableUsers());
$this->assertEquals($storage->getApplicableGroups(), $newStorage->getApplicableGroups());
@@ -148,7 +149,8 @@ class GlobalStoragesServiceTest extends StoragesServiceTest {
/**
* @dataProvider storageDataProvider
*/
public function testUpdateStorage($updatedStorage) {
public function testUpdateStorage($updatedStorageParams) {
$updatedStorage = $this->makeStorageConfig($updatedStorageParams);
$storage = $this->makeStorageConfig([
'mountPoint' => 'mountpoint',
'backendClass' => '\OC\Files\Storage\SMB',

+ 44
- 3
apps/files_external/tests/service/storagesservicetest.php View File

@@ -24,6 +24,7 @@ use \OC\Files\Filesystem;

use \OCA\Files_external\NotFoundException;
use \OCA\Files_external\Lib\StorageConfig;
use \OCA\Files_External\Lib\BackendService;

abstract class StoragesServiceTest extends \Test\TestCase {

@@ -32,6 +33,9 @@ abstract class StoragesServiceTest extends \Test\TestCase {
*/
protected $service;

/** @var BackendService */
protected $backendService;

/**
* Data directory
*
@@ -55,6 +59,25 @@ abstract class StoragesServiceTest extends \Test\TestCase {
);
\OC_Mount_Config::$skipTest = true;

$this->backendService =
$this->getMockBuilder('\OCA\Files_External\Service\BackendService')
->disableOriginalConstructor()
->getMock();

$backends = [
'\OC\Files\Storage\SMB' => $this->getBackendMock('\OCA\Files_External\Lib\Backend\SMB', '\OC\Files\Storage\SMB'),
'\OC\Files\Storage\SFTP' => $this->getBackendMock('\OCA\Files_External\Lib\Backend\SFTP', '\OC\Files\Storage\SFTP'),
];
$this->backendService->method('getBackend')
->will($this->returnCallback(function($backendClass) use ($backends) {
if (isset($backends[$backendClass])) {
return $backends[$backendClass];
}
return null;
}));
$this->backendService->method('getBackends')
->will($this->returnValue($backends));

\OCP\Util::connectHook(
Filesystem::CLASSNAME,
Filesystem::signal_create_mount,
@@ -71,6 +94,17 @@ abstract class StoragesServiceTest extends \Test\TestCase {
self::$hookCalls = array();
}

protected function getBackendMock($class = '\OCA\Files_External\Lib\Backend\SMB', $storageClass = '\OC\Files\Storage\SMB') {
$backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend')
->disableOriginalConstructor()
->getMock();
$backend->method('getStorageClass')
->willReturn($storageClass);
$backend->method('getClass')
->willReturn($storageClass);
return $backend;
}

/**
* Creates a StorageConfig instance based on array data
*
@@ -84,7 +118,12 @@ abstract class StoragesServiceTest extends \Test\TestCase {
$storage->setId($data['id']);
}
$storage->setMountPoint($data['mountPoint']);
$storage->setBackendClass($data['backendClass']);
if (!isset($data['backend'])) {
// data providers are run before $this->backendService is initialised
// so $data['backend'] can be specified directly
$data['backend'] = $this->backendService->getBackend($data['backendClass']);
}
$storage->setBackend($data['backend']);
$storage->setBackendOptions($data['backendOptions']);
if (isset($data['applicableUsers'])) {
$storage->setApplicableUsers($data['applicableUsers']);
@@ -106,16 +145,18 @@ abstract class StoragesServiceTest extends \Test\TestCase {
* @expectedException \OCA\Files_external\NotFoundException
*/
public function testNonExistingStorage() {
$backend = $this->backendService->getBackend('\OC\Files\Storage\SMB');
$storage = new StorageConfig(255);
$storage->setMountPoint('mountpoint');
$storage->setBackendClass('\OC\Files\Storage\SMB');
$storage->setBackend($backend);
$this->service->updateStorage($storage);
}

public function testDeleteStorage() {
$backend = $this->backendService->getBackend('\OC\Files\Storage\SMB');
$storage = new StorageConfig(255);
$storage->setMountPoint('mountpoint');
$storage->setBackendClass('\OC\Files\Storage\SMB');
$storage->setBackend($backend);
$storage->setBackendOptions(['password' => 'testPassword']);

$newStorage = $this->service->addStorage($storage);

+ 2
- 2
apps/files_external/tests/service/userstoragesservicetest.php View File

@@ -40,7 +40,7 @@ class UserStoragesServiceTest extends StoragesServiceTest {
->method('getUser')
->will($this->returnValue($this->user));

$this->service = new UserStoragesService($userSession);
$this->service = new UserStoragesService($this->backendService, $userSession);

// create home folder
mkdir($this->dataDir . '/' . $this->userId . '/');
@@ -76,7 +76,7 @@ class UserStoragesServiceTest extends StoragesServiceTest {
$newStorage = $this->service->getStorage(1);

$this->assertEquals($storage->getMountPoint(), $newStorage->getMountPoint());
$this->assertEquals($storage->getBackendClass(), $newStorage->getBackendClass());
$this->assertEquals($storage->getBackend(), $newStorage->getBackend());
$this->assertEquals($storage->getBackendOptions(), $newStorage->getBackendOptions());
$this->assertEquals(1, $newStorage->getId());
$this->assertEquals(0, $newStorage->getStatus());

+ 7
- 1
apps/files_external/tests/storageconfigtest.php View File

@@ -26,9 +26,15 @@ use \OCA\Files_external\Lib\StorageConfig;
class StorageConfigTest extends \Test\TestCase {

public function testJsonSerialization() {
$backend = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend')
->disableOriginalConstructor()
->getMock();
$backend->method('getClass')
->willReturn('\OC\Files\Storage\SMB');

$storageConfig = new StorageConfig(1);
$storageConfig->setMountPoint('test');
$storageConfig->setBackendClass('\OC\Files\Storage\SMB');
$storageConfig->setBackend($backend);
$storageConfig->setBackendOptions(['user' => 'test', 'password' => 'password123']);
$storageConfig->setPriority(128);
$storageConfig->setApplicableUsers(['user1', 'user2']);

Loading…
Cancel
Save