Merge pull request #17625 from nextcloud/enh/noid/direct-editing
Direct editing API to allow file editing using a one-time token
This commit is contained in:
commit
4173d9d749
|
@ -5,7 +5,7 @@
|
|||
<name>Files</name>
|
||||
<summary>File Management</summary>
|
||||
<description>File Management</description>
|
||||
<version>1.13.0</version>
|
||||
<version>1.13.1</version>
|
||||
<licence>agpl</licence>
|
||||
<author>Robin Appelman</author>
|
||||
<author>Vincent Petry</author>
|
||||
|
@ -26,6 +26,7 @@
|
|||
<job>OCA\Files\BackgroundJob\ScanFiles</job>
|
||||
<job>OCA\Files\BackgroundJob\DeleteOrphanedItems</job>
|
||||
<job>OCA\Files\BackgroundJob\CleanupFileLocks</job>
|
||||
<job>OCA\Files\BackgroundJob\CleanupDirectEditingTokens</job>
|
||||
</background-jobs>
|
||||
|
||||
<commands>
|
||||
|
|
|
@ -92,6 +92,33 @@ $application->registerRoutes(
|
|||
'url' => '/api/v1/quickaccess/get/NodeType',
|
||||
'verb' => 'GET',
|
||||
],
|
||||
[
|
||||
'name' => 'DirectEditingView#edit',
|
||||
'url' => '/directEditing/{token}',
|
||||
'verb' => 'GET'
|
||||
],
|
||||
],
|
||||
'ocs' => [
|
||||
[
|
||||
'name' => 'DirectEditing#info',
|
||||
'url' => '/api/v1/directEditing',
|
||||
'verb' => 'GET'
|
||||
],
|
||||
[
|
||||
'name' => 'DirectEditing#templates',
|
||||
'url' => '/api/v1/directEditing/templates/{editorId}/{creatorId}',
|
||||
'verb' => 'GET'
|
||||
],
|
||||
[
|
||||
'name' => 'DirectEditing#open',
|
||||
'url' => '/api/v1/directEditing/open',
|
||||
'verb' => 'POST'
|
||||
],
|
||||
[
|
||||
'name' => 'DirectEditing#create',
|
||||
'url' => '/api/v1/directEditing/create',
|
||||
'verb' => 'POST'
|
||||
],
|
||||
]
|
||||
]
|
||||
);
|
||||
|
|
|
@ -19,6 +19,7 @@ return array(
|
|||
'OCA\\Files\\Activity\\Settings\\FileRestored' => $baseDir . '/../lib/Activity/Settings/FileRestored.php',
|
||||
'OCA\\Files\\App' => $baseDir . '/../lib/App.php',
|
||||
'OCA\\Files\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
|
||||
'OCA\\Files\\BackgroundJob\\CleanupDirectEditingTokens' => $baseDir . '/../lib/BackgroundJob/CleanupDirectEditingTokens.php',
|
||||
'OCA\\Files\\BackgroundJob\\CleanupFileLocks' => $baseDir . '/../lib/BackgroundJob/CleanupFileLocks.php',
|
||||
'OCA\\Files\\BackgroundJob\\DeleteOrphanedItems' => $baseDir . '/../lib/BackgroundJob/DeleteOrphanedItems.php',
|
||||
'OCA\\Files\\BackgroundJob\\ScanFiles' => $baseDir . '/../lib/BackgroundJob/ScanFiles.php',
|
||||
|
@ -31,12 +32,15 @@ return array(
|
|||
'OCA\\Files\\Command\\TransferOwnership' => $baseDir . '/../lib/Command/TransferOwnership.php',
|
||||
'OCA\\Files\\Controller\\AjaxController' => $baseDir . '/../lib/Controller/AjaxController.php',
|
||||
'OCA\\Files\\Controller\\ApiController' => $baseDir . '/../lib/Controller/ApiController.php',
|
||||
'OCA\\Files\\Controller\\DirectEditingController' => $baseDir . '/../lib/Controller/DirectEditingController.php',
|
||||
'OCA\\Files\\Controller\\DirectEditingViewController' => $baseDir . '/../lib/Controller/DirectEditingViewController.php',
|
||||
'OCA\\Files\\Controller\\ViewController' => $baseDir . '/../lib/Controller/ViewController.php',
|
||||
'OCA\\Files\\Event\\LoadAdditionalScriptsEvent' => $baseDir . '/../lib/Event/LoadAdditionalScriptsEvent.php',
|
||||
'OCA\\Files\\Event\\LoadSidebar' => $baseDir . '/../lib/Event/LoadSidebar.php',
|
||||
'OCA\\Files\\Exception\\TransferOwnershipException' => $baseDir . '/../lib/Exception/TransferOwnershipException.php',
|
||||
'OCA\\Files\\Helper' => $baseDir . '/../lib/Helper.php',
|
||||
'OCA\\Files\\Listener\\LegacyLoadAdditionalScriptsAdapter' => $baseDir . '/../lib/Listener/LegacyLoadAdditionalScriptsAdapter.php',
|
||||
'OCA\\Files\\Service\\DirectEditingService' => $baseDir . '/../lib/Service/DirectEditingService.php',
|
||||
'OCA\\Files\\Service\\OwnershipTransferService' => $baseDir . '/../lib/Service/OwnershipTransferService.php',
|
||||
'OCA\\Files\\Service\\TagService' => $baseDir . '/../lib/Service/TagService.php',
|
||||
);
|
||||
|
|
|
@ -34,6 +34,7 @@ class ComposerStaticInitFiles
|
|||
'OCA\\Files\\Activity\\Settings\\FileRestored' => __DIR__ . '/..' . '/../lib/Activity/Settings/FileRestored.php',
|
||||
'OCA\\Files\\App' => __DIR__ . '/..' . '/../lib/App.php',
|
||||
'OCA\\Files\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
|
||||
'OCA\\Files\\BackgroundJob\\CleanupDirectEditingTokens' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupDirectEditingTokens.php',
|
||||
'OCA\\Files\\BackgroundJob\\CleanupFileLocks' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupFileLocks.php',
|
||||
'OCA\\Files\\BackgroundJob\\DeleteOrphanedItems' => __DIR__ . '/..' . '/../lib/BackgroundJob/DeleteOrphanedItems.php',
|
||||
'OCA\\Files\\BackgroundJob\\ScanFiles' => __DIR__ . '/..' . '/../lib/BackgroundJob/ScanFiles.php',
|
||||
|
@ -46,12 +47,15 @@ class ComposerStaticInitFiles
|
|||
'OCA\\Files\\Command\\TransferOwnership' => __DIR__ . '/..' . '/../lib/Command/TransferOwnership.php',
|
||||
'OCA\\Files\\Controller\\AjaxController' => __DIR__ . '/..' . '/../lib/Controller/AjaxController.php',
|
||||
'OCA\\Files\\Controller\\ApiController' => __DIR__ . '/..' . '/../lib/Controller/ApiController.php',
|
||||
'OCA\\Files\\Controller\\DirectEditingController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingController.php',
|
||||
'OCA\\Files\\Controller\\DirectEditingViewController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingViewController.php',
|
||||
'OCA\\Files\\Controller\\ViewController' => __DIR__ . '/..' . '/../lib/Controller/ViewController.php',
|
||||
'OCA\\Files\\Event\\LoadAdditionalScriptsEvent' => __DIR__ . '/..' . '/../lib/Event/LoadAdditionalScriptsEvent.php',
|
||||
'OCA\\Files\\Event\\LoadSidebar' => __DIR__ . '/..' . '/../lib/Event/LoadSidebar.php',
|
||||
'OCA\\Files\\Exception\\TransferOwnershipException' => __DIR__ . '/..' . '/../lib/Exception/TransferOwnershipException.php',
|
||||
'OCA\\Files\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php',
|
||||
'OCA\\Files\\Listener\\LegacyLoadAdditionalScriptsAdapter' => __DIR__ . '/..' . '/../lib/Listener/LegacyLoadAdditionalScriptsAdapter.php',
|
||||
'OCA\\Files\\Service\\DirectEditingService' => __DIR__ . '/..' . '/../lib/Service/DirectEditingService.php',
|
||||
'OCA\\Files\\Service\\OwnershipTransferService' => __DIR__ . '/..' . '/../lib/Service/OwnershipTransferService.php',
|
||||
'OCA\\Files\\Service\\TagService' => __DIR__ . '/..' . '/../lib/Service/TagService.php',
|
||||
);
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Files\BackgroundJob;
|
||||
|
||||
use OC\BackgroundJob\TimedJob;
|
||||
use OCP\DirectEditing\IManager;
|
||||
|
||||
class CleanupDirectEditingTokens extends TimedJob {
|
||||
|
||||
private const INTERVAL_MINUTES = 15 * 60;
|
||||
|
||||
/**
|
||||
* @var IManager
|
||||
*/
|
||||
private $manager;
|
||||
|
||||
public function __construct(IManager $manager) {
|
||||
$this->interval = self::INTERVAL_MINUTES;
|
||||
$this->manager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the background job do its work
|
||||
*
|
||||
* @param array $argument unused argument
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function run($argument) {
|
||||
$this->manager->cleanup();
|
||||
}
|
||||
}
|
|
@ -25,8 +25,16 @@
|
|||
|
||||
namespace OCA\Files;
|
||||
|
||||
use OC\DirectEditing\Manager;
|
||||
use OCA\Files\Service\DirectEditingService;
|
||||
use OCP\Capabilities\ICapability;
|
||||
use OCP\DirectEditing\ACreateEmpty;
|
||||
use OCP\DirectEditing\ACreateFromTemplate;
|
||||
use OCP\DirectEditing\IEditor;
|
||||
use OCP\DirectEditing\RegisterDirectEditorEvent;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IConfig;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
/**
|
||||
* Class Capabilities
|
||||
|
@ -34,16 +42,25 @@ use OCP\IConfig;
|
|||
* @package OCA\Files
|
||||
*/
|
||||
class Capabilities implements ICapability {
|
||||
|
||||
/** @var IConfig */
|
||||
protected $config;
|
||||
|
||||
/** @var DirectEditingService */
|
||||
protected $directEditingService;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
/**
|
||||
* Capabilities constructor.
|
||||
*
|
||||
* @param IConfig $config
|
||||
*/
|
||||
public function __construct(IConfig $config) {
|
||||
public function __construct(IConfig $config, DirectEditingService $directEditingService, IURLGenerator $urlGenerator) {
|
||||
$this->config = $config;
|
||||
$this->directEditingService = $directEditingService;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,7 +73,13 @@ class Capabilities implements ICapability {
|
|||
'files' => [
|
||||
'bigfilechunking' => true,
|
||||
'blacklisted_files' => $this->config->getSystemValue('blacklisted_files', ['.htaccess']),
|
||||
'directEditing' => [
|
||||
'url' => $this->urlGenerator->linkToOCSRouteAbsolute('files.DirectEditing.info'),
|
||||
'etag' => $this->directEditingService->getDirectEditingETag()
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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\Files\Controller;
|
||||
|
||||
|
||||
use Exception;
|
||||
use OCA\Files\Service\DirectEditingService;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\DirectEditing\ACreateEmpty;
|
||||
use OCP\DirectEditing\ACreateFromTemplate;
|
||||
use OCP\DirectEditing\IEditor;
|
||||
use OCP\DirectEditing\IManager;
|
||||
use OCP\DirectEditing\RegisterDirectEditorEvent;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\ILogger;
|
||||
use OCP\IRequest;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
class DirectEditingController extends OCSController {
|
||||
|
||||
/** @var IEventDispatcher */
|
||||
private $eventDispatcher;
|
||||
|
||||
/** @var IManager */
|
||||
private $directEditingManager;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
/** @var DirectEditingService */
|
||||
private $directEditingService;
|
||||
|
||||
public function __construct($appName, IRequest $request, $corsMethods, $corsAllowedHeaders, $corsMaxAge,
|
||||
IEventDispatcher $eventDispatcher, IURLGenerator $urlGenerator, IManager $manager, DirectEditingService $directEditingService, ILogger $logger) {
|
||||
parent::__construct($appName, $request, $corsMethods, $corsAllowedHeaders, $corsMaxAge);
|
||||
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->directEditingManager = $manager;
|
||||
$this->directEditingService = $directEditingService;
|
||||
$this->logger = $logger;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function info(): DataResponse {
|
||||
$response = new DataResponse($this->directEditingService->getDirectEditingCapabilitites());
|
||||
$response->setETag($this->directEditingService->getDirectEditingETag());
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function create(string $path, string $editorId, string $creatorId, string $templateId = null): DataResponse {
|
||||
$this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
|
||||
|
||||
try {
|
||||
$token = $this->directEditingManager->create($path, $editorId, $creatorId, $templateId);
|
||||
return new DataResponse([
|
||||
'url' => $this->urlGenerator->linkToRouteAbsolute('files.DirectEditingView.edit', ['token' => $token])
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
$this->logger->logException($e, ['message' => 'Exception when creating a new file through direct editing']);
|
||||
return new DataResponse('Failed to create file', Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function open(int $fileId, string $editorId = null): DataResponse {
|
||||
$this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
|
||||
|
||||
try {
|
||||
$token = $this->directEditingManager->open($fileId, $editorId);
|
||||
return new DataResponse([
|
||||
'url' => $this->urlGenerator->linkToRouteAbsolute('files.DirectEditingView.edit', ['token' => $token])
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
$this->logger->logException($e, ['message' => 'Exception when opening a file through direct editing']);
|
||||
return new DataResponse('Failed to open file', Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function templates(string $editorId, string $creatorId): DataResponse {
|
||||
$this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
|
||||
|
||||
try {
|
||||
return new DataResponse($this->directEditingManager->getTemplates($editorId, $creatorId));
|
||||
} catch (Exception $e) {
|
||||
$this->logger->logException($e);
|
||||
return new DataResponse('Failed to open file', Http::STATUS_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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\Files\Controller;
|
||||
|
||||
|
||||
use Exception;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\NotFoundResponse;
|
||||
use OCP\AppFramework\Http\Response;
|
||||
use OCP\DirectEditing\IManager;
|
||||
use OCP\DirectEditing\RegisterDirectEditorEvent;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\ILogger;
|
||||
use OCP\IRequest;
|
||||
|
||||
class DirectEditingViewController extends Controller {
|
||||
|
||||
/** @var IEventDispatcher */
|
||||
private $eventDispatcher;
|
||||
|
||||
/** @var IManager */
|
||||
private $directEditingManager;
|
||||
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
public function __construct($appName, IRequest $request, IEventDispatcher $eventDispatcher, IManager $manager, ILogger $logger) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->directEditingManager = $manager;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @param string $token
|
||||
* @return Response
|
||||
*/
|
||||
public function edit(string $token): Response {
|
||||
$this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
|
||||
try {
|
||||
return $this->directEditingManager->edit($token);
|
||||
} catch (Exception $e) {
|
||||
$this->logger->logException($e);
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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\Files\Service;
|
||||
|
||||
|
||||
use OCP\DirectEditing\ACreateEmpty;
|
||||
use OCP\DirectEditing\ACreateFromTemplate;
|
||||
use OCP\DirectEditing\IEditor;
|
||||
use OCP\DirectEditing\IManager;
|
||||
use OCP\DirectEditing\RegisterDirectEditorEvent;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
|
||||
class DirectEditingService {
|
||||
|
||||
/** @var IManager */
|
||||
private $directEditingManager;
|
||||
/** @var IEventDispatcher */
|
||||
private $eventDispatcher;
|
||||
|
||||
public function __construct(IEventDispatcher $eventDispatcher, IManager $directEditingManager) {
|
||||
$this->directEditingManager = $directEditingManager;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
public function getDirectEditingETag(): string {
|
||||
return \md5(\json_encode($this->getDirectEditingCapabilitites()));
|
||||
}
|
||||
|
||||
public function getDirectEditingCapabilitites(): array {
|
||||
$this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
|
||||
|
||||
$capabilities = [
|
||||
'editors' => [],
|
||||
'creators' => []
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string $id
|
||||
* @var IEditor $editor
|
||||
*/
|
||||
foreach ($this->directEditingManager->getEditors() as $id => $editor) {
|
||||
$capabilities['editors'][$id] = [
|
||||
'name' => $editor->getName(),
|
||||
'mimetypes' => $editor->getMimetypes(),
|
||||
'optionalMimetypes' => $editor->getMimetypesOptional(),
|
||||
'secure' => $editor->isSecure(),
|
||||
];
|
||||
/** @var ACreateEmpty|ACreateFromTemplate $creator */
|
||||
foreach ($editor->getCreators() as $creator) {
|
||||
$id = $creator->getId();
|
||||
$capabilities['creators'][$id] = [
|
||||
'id' => $id,
|
||||
'editor' => $editor->getId(),
|
||||
'name' => $creator->getName(),
|
||||
'extension' => $creator->getExtension(),
|
||||
'templates' => $creator instanceof ACreateFromTemplate,
|
||||
'mimetype' => $creator->getMimetype()
|
||||
];
|
||||
}
|
||||
}
|
||||
return $capabilities;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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 OC\Core\Migrations;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
use OCP\Migration\IOutput;
|
||||
|
||||
class Version18000Date20191014105105 extends SimpleMigrationStep {
|
||||
|
||||
/** @var IDBConnection */
|
||||
protected $connection;
|
||||
|
||||
public function __construct(IDBConnection $connection) {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
$table = $schema->createTable('direct_edit');
|
||||
|
||||
$table->addColumn('id', Type::BIGINT, [
|
||||
'autoincrement' => true,
|
||||
'notnull' => true,
|
||||
]);
|
||||
$table->addColumn('editor_id', Type::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 64,
|
||||
]);
|
||||
$table->addColumn('token', Type::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 64,
|
||||
]);
|
||||
$table->addColumn('file_id', Type::BIGINT, [
|
||||
'notnull' => true,
|
||||
]);
|
||||
$table->addColumn('user_id', Type::STRING, [
|
||||
'notnull' => false,
|
||||
'length' => 64,
|
||||
]);
|
||||
$table->addColumn('share_id', Type::BIGINT, [
|
||||
'notnull' => false
|
||||
]);
|
||||
$table->addColumn('timestamp', Type::BIGINT, [
|
||||
'notnull' => true,
|
||||
'length' => 20,
|
||||
'unsigned' => true,
|
||||
]);
|
||||
$table->addColumn('accessed', Type::BOOLEAN, [
|
||||
'notnull' => true,
|
||||
'default' => false
|
||||
]);
|
||||
|
||||
$table->setPrimaryKey(['id']);
|
||||
$table->addIndex(['token']);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
}
|
|
@ -118,7 +118,8 @@ $application->registerRoutes($this, [
|
|||
|
||||
['root' => '/collaboration', 'name' => 'CollaborationResources#removeResource', 'url' => '/resources/collections/{collectionId}', 'verb' => 'DELETE'],
|
||||
['root' => '/collaboration', 'name' => 'CollaborationResources#getCollectionsByResource', 'url' => '/resources/{resourceType}/{resourceId}', 'verb' => 'GET'],
|
||||
['root' => '/collaboration', 'name' => 'CollaborationResources#createCollectionOnResource', 'url' => '/resources/{baseResourceType}/{baseResourceId}', 'verb' => 'POST'],
|
||||
['root' => '/collaboration', 'name' => 'CollaborationResources#createCollectionOnResource', 'url' => '/resources/{baseResourceType}/{baseResourceId}', 'verb' => 'POST']
|
||||
|
||||
],
|
||||
]);
|
||||
|
||||
|
|
|
@ -170,6 +170,13 @@ return array(
|
|||
'OCP\\Diagnostics\\IEventLogger' => $baseDir . '/lib/public/Diagnostics/IEventLogger.php',
|
||||
'OCP\\Diagnostics\\IQuery' => $baseDir . '/lib/public/Diagnostics/IQuery.php',
|
||||
'OCP\\Diagnostics\\IQueryLogger' => $baseDir . '/lib/public/Diagnostics/IQueryLogger.php',
|
||||
'OCP\\DirectEditing\\ACreateEmpty' => $baseDir . '/lib/public/DirectEditing/ACreateEmpty.php',
|
||||
'OCP\\DirectEditing\\ACreateFromTemplate' => $baseDir . '/lib/public/DirectEditing/ACreateFromTemplate.php',
|
||||
'OCP\\DirectEditing\\ATemplate' => $baseDir . '/lib/public/DirectEditing/ATemplate.php',
|
||||
'OCP\\DirectEditing\\IEditor' => $baseDir . '/lib/public/DirectEditing/IEditor.php',
|
||||
'OCP\\DirectEditing\\IManager' => $baseDir . '/lib/public/DirectEditing/IManager.php',
|
||||
'OCP\\DirectEditing\\IToken' => $baseDir . '/lib/public/DirectEditing/IToken.php',
|
||||
'OCP\\DirectEditing\\RegisterDirectEditorEvent' => $baseDir . '/lib/public/DirectEditing/RegisterDirectEditorEvent.php',
|
||||
'OCP\\Encryption\\Exceptions\\GenericEncryptionException' => $baseDir . '/lib/public/Encryption/Exceptions/GenericEncryptionException.php',
|
||||
'OCP\\Encryption\\IEncryptionModule' => $baseDir . '/lib/public/Encryption/IEncryptionModule.php',
|
||||
'OCP\\Encryption\\IFile' => $baseDir . '/lib/public/Encryption/IFile.php',
|
||||
|
@ -789,6 +796,7 @@ return array(
|
|||
'OC\\Core\\Migrations\\Version16000Date20190428150708' => $baseDir . '/core/Migrations/Version16000Date20190428150708.php',
|
||||
'OC\\Core\\Migrations\\Version17000Date20190514105811' => $baseDir . '/core/Migrations/Version17000Date20190514105811.php',
|
||||
'OC\\Core\\Migrations\\Version18000Date20190920085628' => $baseDir . '/core/Migrations/Version18000Date20190920085628.php',
|
||||
'OC\\Core\\Migrations\\Version18000Date20191014105105' => $baseDir . '/core/Migrations/Version18000Date20191014105105.php',
|
||||
'OC\\Core\\Notification\\RemoveLinkSharesNotifier' => $baseDir . '/core/Notification/RemoveLinkSharesNotifier.php',
|
||||
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',
|
||||
'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php',
|
||||
|
@ -840,6 +848,8 @@ return array(
|
|||
'OC\\Diagnostics\\EventLogger' => $baseDir . '/lib/private/Diagnostics/EventLogger.php',
|
||||
'OC\\Diagnostics\\Query' => $baseDir . '/lib/private/Diagnostics/Query.php',
|
||||
'OC\\Diagnostics\\QueryLogger' => $baseDir . '/lib/private/Diagnostics/QueryLogger.php',
|
||||
'OC\\DirectEditing\\Manager' => $baseDir . '/lib/private/DirectEditing/Manager.php',
|
||||
'OC\\DirectEditing\\Token' => $baseDir . '/lib/private/DirectEditing/Token.php',
|
||||
'OC\\Encryption\\DecryptAll' => $baseDir . '/lib/private/Encryption/DecryptAll.php',
|
||||
'OC\\Encryption\\EncryptionWrapper' => $baseDir . '/lib/private/Encryption/EncryptionWrapper.php',
|
||||
'OC\\Encryption\\Exceptions\\DecryptionFailedException' => $baseDir . '/lib/private/Encryption/Exceptions/DecryptionFailedException.php',
|
||||
|
|
|
@ -199,6 +199,13 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
|||
'OCP\\Diagnostics\\IEventLogger' => __DIR__ . '/../../..' . '/lib/public/Diagnostics/IEventLogger.php',
|
||||
'OCP\\Diagnostics\\IQuery' => __DIR__ . '/../../..' . '/lib/public/Diagnostics/IQuery.php',
|
||||
'OCP\\Diagnostics\\IQueryLogger' => __DIR__ . '/../../..' . '/lib/public/Diagnostics/IQueryLogger.php',
|
||||
'OCP\\DirectEditing\\ACreateEmpty' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/ACreateEmpty.php',
|
||||
'OCP\\DirectEditing\\ACreateFromTemplate' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/ACreateFromTemplate.php',
|
||||
'OCP\\DirectEditing\\ATemplate' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/ATemplate.php',
|
||||
'OCP\\DirectEditing\\IEditor' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/IEditor.php',
|
||||
'OCP\\DirectEditing\\IManager' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/IManager.php',
|
||||
'OCP\\DirectEditing\\IToken' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/IToken.php',
|
||||
'OCP\\DirectEditing\\RegisterDirectEditorEvent' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/RegisterDirectEditorEvent.php',
|
||||
'OCP\\Encryption\\Exceptions\\GenericEncryptionException' => __DIR__ . '/../../..' . '/lib/public/Encryption/Exceptions/GenericEncryptionException.php',
|
||||
'OCP\\Encryption\\IEncryptionModule' => __DIR__ . '/../../..' . '/lib/public/Encryption/IEncryptionModule.php',
|
||||
'OCP\\Encryption\\IFile' => __DIR__ . '/../../..' . '/lib/public/Encryption/IFile.php',
|
||||
|
@ -818,6 +825,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
|||
'OC\\Core\\Migrations\\Version16000Date20190428150708' => __DIR__ . '/../../..' . '/core/Migrations/Version16000Date20190428150708.php',
|
||||
'OC\\Core\\Migrations\\Version17000Date20190514105811' => __DIR__ . '/../../..' . '/core/Migrations/Version17000Date20190514105811.php',
|
||||
'OC\\Core\\Migrations\\Version18000Date20190920085628' => __DIR__ . '/../../..' . '/core/Migrations/Version18000Date20190920085628.php',
|
||||
'OC\\Core\\Migrations\\Version18000Date20191014105105' => __DIR__ . '/../../..' . '/core/Migrations/Version18000Date20191014105105.php',
|
||||
'OC\\Core\\Notification\\RemoveLinkSharesNotifier' => __DIR__ . '/../../..' . '/core/Notification/RemoveLinkSharesNotifier.php',
|
||||
'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',
|
||||
'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php',
|
||||
|
@ -869,6 +877,8 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
|||
'OC\\Diagnostics\\EventLogger' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/EventLogger.php',
|
||||
'OC\\Diagnostics\\Query' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/Query.php',
|
||||
'OC\\Diagnostics\\QueryLogger' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/QueryLogger.php',
|
||||
'OC\\DirectEditing\\Manager' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Manager.php',
|
||||
'OC\\DirectEditing\\Token' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Token.php',
|
||||
'OC\\Encryption\\DecryptAll' => __DIR__ . '/../../..' . '/lib/private/Encryption/DecryptAll.php',
|
||||
'OC\\Encryption\\EncryptionWrapper' => __DIR__ . '/../../..' . '/lib/private/Encryption/EncryptionWrapper.php',
|
||||
'OC\\Encryption\\Exceptions\\DecryptionFailedException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/DecryptionFailedException.php',
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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 OC\DirectEditing;
|
||||
|
||||
use Doctrine\DBAL\FetchMode;
|
||||
use OCP\AppFramework\Http\NotFoundResponse;
|
||||
use OCP\AppFramework\Http\Response;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\DirectEditing\ACreateFromTemplate;
|
||||
use OCP\DirectEditing\IEditor;
|
||||
use \OCP\DirectEditing\IManager;
|
||||
use OCP\DirectEditing\IToken;
|
||||
use OCP\DirectEditing\RegisterDirectEditorEvent;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IUserSession;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use OCP\Share\IShare;
|
||||
|
||||
class Manager implements IManager {
|
||||
|
||||
private const TOKEN_CLEANUP_TIME = 12 * 60 * 60 ;
|
||||
|
||||
public const TABLE_TOKENS = 'direct_edit';
|
||||
|
||||
/** @var IEditor[] */
|
||||
private $editors = [];
|
||||
|
||||
/** @var IDBConnection */
|
||||
private $connection;
|
||||
/**
|
||||
* @var ISecureRandom
|
||||
*/
|
||||
private $random;
|
||||
private $userId;
|
||||
private $rootFolder;
|
||||
|
||||
public function __construct(
|
||||
ISecureRandom $random,
|
||||
IDBConnection $connection,
|
||||
IUserSession $userSession,
|
||||
IRootFolder $rootFolder
|
||||
) {
|
||||
$this->random = $random;
|
||||
$this->connection = $connection;
|
||||
$this->userId = $userSession->getUser() ? $userSession->getUser()->getUID() : null;
|
||||
$this->rootFolder = $rootFolder;
|
||||
}
|
||||
|
||||
public function registerDirectEditor(IEditor $directEditor): void {
|
||||
$this->editors[$directEditor->getId()] = $directEditor;
|
||||
}
|
||||
|
||||
public function getEditors(): array {
|
||||
return $this->editors;
|
||||
}
|
||||
|
||||
public function getTemplates(string $editor, string $type): array {
|
||||
if (!array_key_exists($editor, $this->editors)) {
|
||||
throw new \RuntimeException('No matching editor found');
|
||||
}
|
||||
$templates = [];
|
||||
foreach ($this->editors[$editor]->getCreators() as $creator) {
|
||||
if ($creator instanceof ACreateFromTemplate && $creator->getId() === $type) {
|
||||
$templates = $creator->getTemplates();
|
||||
}
|
||||
}
|
||||
$return = [];
|
||||
$return['templates'] = $templates;
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function create(string $path, string $editorId, string $creatorId, $templateId = null): string {
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->userId);
|
||||
$file = $userFolder->newFile($path);
|
||||
$editor = $this->getEditor($editorId);
|
||||
$creators = $editor->getCreators();
|
||||
foreach ($creators as $creator) {
|
||||
if ($creator->getId() === $creatorId) {
|
||||
$creator->create($file, $creatorId, $templateId);
|
||||
return $this->createToken($editorId, $file);
|
||||
}
|
||||
}
|
||||
throw new \RuntimeException('No creator found');
|
||||
}
|
||||
|
||||
public function open(int $fileId, string $editorId = null): string {
|
||||
$file = $this->rootFolder->getUserFolder($this->userId)->getById($fileId);
|
||||
if (count($file) === 0 || !($file[0] instanceof File) || $file === null) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
/** @var File $file */
|
||||
$file = $file[0];
|
||||
|
||||
if ($editorId === null) {
|
||||
$editorId = $this->findEditorForFile($file);
|
||||
}
|
||||
|
||||
return $this->createToken($editorId, $file);
|
||||
}
|
||||
|
||||
private function findEditorForFile(File $file) {
|
||||
foreach ($this->editors as $editor) {
|
||||
if (in_array($file->getMimeType(), $editor->getMimetypes())) {
|
||||
return $editor->getId();
|
||||
}
|
||||
}
|
||||
throw new \RuntimeException('No default editor found for files mimetype');
|
||||
}
|
||||
|
||||
public function edit(string $token): Response {
|
||||
try {
|
||||
/** @var IEditor $editor */
|
||||
$tokenObject = $this->getToken($token);
|
||||
if ($tokenObject->hasBeenAccessed()) {
|
||||
throw new \RuntimeException('Token has already been used and can only be used for followup requests');
|
||||
}
|
||||
$editor = $this->getEditor($tokenObject->getEditor());
|
||||
$this->accessToken($token);
|
||||
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->invalidateToken($token);
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
return $editor->open($tokenObject);
|
||||
}
|
||||
|
||||
public function editSecure(File $file, string $editorId): TemplateResponse {
|
||||
// TODO: Implementation in follow up
|
||||
}
|
||||
|
||||
private function getEditor($editorId): IEditor {
|
||||
if (!array_key_exists($editorId, $this->editors)) {
|
||||
throw new \RuntimeException('No editor found');
|
||||
}
|
||||
return $this->editors[$editorId];
|
||||
}
|
||||
|
||||
public function getToken(string $token): IToken {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->select('*')->from(self::TABLE_TOKENS)
|
||||
->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
|
||||
$result = $query->execute();
|
||||
if ($tokenRow = $result->fetch(FetchMode::ASSOCIATIVE)) {
|
||||
return new Token($this, $tokenRow);
|
||||
}
|
||||
throw new \RuntimeException('Failed to validate the token');
|
||||
}
|
||||
|
||||
public function cleanup(): int {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->delete(self::TABLE_TOKENS)
|
||||
->where($query->expr()->lt('timestamp', $query->createNamedParameter(time() - self::TOKEN_CLEANUP_TIME)));
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
public function refreshToken(string $token): bool {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->update(self::TABLE_TOKENS)
|
||||
->set('timestamp', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
|
||||
->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
|
||||
$result = $query->execute();
|
||||
return $result !== 0;
|
||||
}
|
||||
|
||||
|
||||
public function invalidateToken(string $token): bool {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->delete(self::TABLE_TOKENS)
|
||||
->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
|
||||
$result = $query->execute();
|
||||
return $result !== 0;
|
||||
}
|
||||
|
||||
public function accessToken(string $token): bool {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->update(self::TABLE_TOKENS)
|
||||
->set('accessed', $query->createNamedParameter(true, IQueryBuilder::PARAM_BOOL))
|
||||
->set('timestamp', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
|
||||
->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
|
||||
$result = $query->execute();
|
||||
return $result !== 0;
|
||||
}
|
||||
|
||||
public function invokeTokenScope($userId): void {
|
||||
\OC_User::setIncognitoMode(true);
|
||||
\OC_User::setUserId($userId);
|
||||
}
|
||||
|
||||
public function createToken($editorId, File $file, IShare $share = null): string {
|
||||
$token = $this->random->generate(64, ISecureRandom::CHAR_HUMAN_READABLE);
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->insert(self::TABLE_TOKENS)
|
||||
->values([
|
||||
'token' => $query->createNamedParameter($token),
|
||||
'editor_id' => $query->createNamedParameter($editorId),
|
||||
'file_id' => $query->createNamedParameter($file->getId()),
|
||||
'user_id' => $query->createNamedParameter($this->userId),
|
||||
'share_id' => $query->createNamedParameter($share !== null ? $share->getId(): null),
|
||||
'timestamp' => $query->createNamedParameter(time())
|
||||
]);
|
||||
$query->execute();
|
||||
return $token;
|
||||
}
|
||||
|
||||
public function getFileForToken($userId, $fileId) {
|
||||
$userFolder = $this->rootFolder->getUserFolder($userId);
|
||||
return $userFolder->getById($fileId)[0];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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 OC\DirectEditing;
|
||||
|
||||
|
||||
use OCP\DirectEditing\IToken;
|
||||
use OCP\Files\File;
|
||||
|
||||
class Token implements IToken {
|
||||
|
||||
/** @var Manager */
|
||||
private $manager;
|
||||
private $data;
|
||||
|
||||
public function __construct(Manager $manager, $data) {
|
||||
$this->manager = $manager;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function extend(): void {
|
||||
$this->manager->refreshToken($this->data['token']);
|
||||
}
|
||||
|
||||
public function invalidate(): void {
|
||||
$this->manager->invalidateToken($this->data['token']);
|
||||
}
|
||||
|
||||
public function getFile(): File {
|
||||
if ($this->data['share_id'] !== null) {
|
||||
return $this->manager->getShareForToken($this->data['share_id']);
|
||||
}
|
||||
return $this->manager->getFileForToken($this->data['user_id'], $this->data['file_id']);
|
||||
}
|
||||
|
||||
public function getToken(): string {
|
||||
return $this->data['token'];
|
||||
}
|
||||
|
||||
public function useTokenScope(): void {
|
||||
$this->manager->invokeTokenScope($this->data['user_id']);
|
||||
}
|
||||
|
||||
public function hasBeenAccessed(): bool {
|
||||
return $this->data['accessed'] === '1';
|
||||
}
|
||||
|
||||
public function getEditor(): string {
|
||||
return $this->data['editor_id'];
|
||||
}
|
||||
|
||||
public function getUser(): string {
|
||||
return $this->data['user_id'];
|
||||
}
|
||||
|
||||
}
|
|
@ -199,6 +199,8 @@ class Server extends ServerContainer implements IServerContainer {
|
|||
$this->registerAlias(\OCP\Contacts\IManager::class, \OC\ContactsManager::class);
|
||||
$this->registerAlias('ContactsManager', \OCP\Contacts\IManager::class);
|
||||
|
||||
$this->registerAlias(\OCP\DirectEditing\IManager::class, \OC\DirectEditing\Manager::class);
|
||||
|
||||
$this->registerAlias(IActionFactory::class, ActionFactory::class);
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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 OCP\DirectEditing;
|
||||
|
||||
|
||||
use OCP\Files\File;
|
||||
|
||||
/**
|
||||
* @since 18.0.0
|
||||
*/
|
||||
abstract class ACreateEmpty {
|
||||
|
||||
/**
|
||||
* Unique id for the creator to filter templates
|
||||
*
|
||||
* e.g. document/spreadsheet/presentation
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getId(): string;
|
||||
|
||||
/**
|
||||
* Descriptive name for the create action
|
||||
*
|
||||
* e.g Create a new document
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getName(): string;
|
||||
|
||||
/**
|
||||
* Default file extension for the new file
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getExtension(): string;
|
||||
|
||||
/**
|
||||
* Mimetype of the resulting created file
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getMimetype(): string;
|
||||
|
||||
/**
|
||||
* Add content when creating empty files
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @param File $file
|
||||
*/
|
||||
public function create(File $file, string $creatorId = null, string $templateId = null): void {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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 OCP\DirectEditing;
|
||||
|
||||
/**
|
||||
* @since 18.0.0
|
||||
*/
|
||||
abstract class ACreateFromTemplate extends ACreateEmpty {
|
||||
|
||||
/**
|
||||
* List of available templates for the create from template action
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return ATemplate[]
|
||||
*/
|
||||
abstract public function getTemplates(): array;
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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 OCP\DirectEditing;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
/**
|
||||
* Class ATemplate
|
||||
*
|
||||
* @package OCP\DirectEditing
|
||||
* @since 18.0.0
|
||||
*/
|
||||
abstract class ATemplate implements JsonSerializable {
|
||||
|
||||
/**
|
||||
* Return a unique id so the app can identify the template
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getId(): string;
|
||||
|
||||
/**
|
||||
* Return a title that is displayed to the user
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getTitle(): string;
|
||||
|
||||
/**
|
||||
* Return a link to the template preview image
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getPreview(): string;
|
||||
|
||||
/**
|
||||
* @since 18.0.0
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function jsonSerialize() {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'title' => $this->getTitle(),
|
||||
'preview' => $this->getPreview(),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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 OCP\DirectEditing;
|
||||
|
||||
|
||||
use OCP\AppFramework\Http\Response;
|
||||
|
||||
/**
|
||||
* @since 18.0.0
|
||||
*/
|
||||
interface IEditor {
|
||||
|
||||
/**
|
||||
* Return a unique identifier for the editor
|
||||
*
|
||||
* e.g. richdocuments
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return string
|
||||
*/
|
||||
public function getId(): string;
|
||||
|
||||
/**
|
||||
* Return a readable name for the editor
|
||||
*
|
||||
* e.g. Collabora Online
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string;
|
||||
|
||||
/**
|
||||
* A list of mimetypes that should open the editor by default
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return string[]
|
||||
*/
|
||||
public function getMimetypes(): array;
|
||||
|
||||
/**
|
||||
* A list of mimetypes that can be opened in the editor optionally
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return string[]
|
||||
*/
|
||||
public function getMimetypesOptional(): array;
|
||||
|
||||
/**
|
||||
* Return a list of file creation options to be presented to the user
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return ACreateFromTemplate[]|ACreateEmpty[]
|
||||
*/
|
||||
public function getCreators(): array;
|
||||
|
||||
/**
|
||||
* Return if the view is able to securely view a file without downloading it to the browser
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return bool
|
||||
*/
|
||||
public function isSecure(): bool;
|
||||
|
||||
/**
|
||||
* Return a template response for displaying the editor
|
||||
*
|
||||
* open can only be called once when the client requests the editor with a one-time-use token
|
||||
* For handling editing and later requests, editors need to impelement their own token handling and take care of invalidation
|
||||
*
|
||||
* This behavior is similar to the current direct editing implementation in collabora where we generate a one-time token and switch over to the regular wopi token for the actual editing/saving process
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return Response
|
||||
*/
|
||||
public function open(IToken $token): Response;
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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 OCP\DirectEditing;
|
||||
|
||||
use OCP\AppFramework\Http\Response;
|
||||
use OCP\Files\NotPermittedException;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Interface IManager
|
||||
*
|
||||
* @package OCP\DirectEditing
|
||||
* @since 18.0.0
|
||||
*/
|
||||
interface IManager {
|
||||
|
||||
/**
|
||||
* Register a new editor
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @param IEditor $directEditor
|
||||
*/
|
||||
public function registerDirectEditor(IEditor $directEditor): void;
|
||||
|
||||
/**
|
||||
* Open the editing page for a provided token
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @param string $token
|
||||
* @return Response
|
||||
*/
|
||||
public function edit(string $token): Response;
|
||||
|
||||
/**
|
||||
* Create a new token based on the file path and editor details
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @param string $path
|
||||
* @param string $editorId
|
||||
* @param string $creatorId
|
||||
* @param null $templateId
|
||||
* @return string
|
||||
* @throws NotPermittedException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function create(string $path, string $editorId, string $creatorId, $templateId = null): string;
|
||||
|
||||
/**
|
||||
* Get the token details for a given token
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @param string $token
|
||||
* @return IToken
|
||||
*/
|
||||
public function getToken(string $token): IToken;
|
||||
|
||||
/**
|
||||
* Cleanup expired tokens
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return int number of deleted tokens
|
||||
*/
|
||||
public function cleanup(): int;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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 OCP\DirectEditing;
|
||||
|
||||
|
||||
use OCP\Files\File;
|
||||
|
||||
/**
|
||||
* @since 18.0.0
|
||||
*/
|
||||
interface IToken {
|
||||
|
||||
/**
|
||||
* Extend the token validity time
|
||||
*
|
||||
* @since 18.0.0
|
||||
*/
|
||||
public function extend(): void;
|
||||
|
||||
/**
|
||||
* Invalidate the token
|
||||
*
|
||||
* @since 18.0.0
|
||||
*/
|
||||
public function invalidate(): void;
|
||||
|
||||
/**
|
||||
* Check if the token has already been used
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBeenAccessed(): bool;
|
||||
|
||||
/**
|
||||
* Change to the user scope of the token
|
||||
*
|
||||
* @since 18.0.0
|
||||
*/
|
||||
public function useTokenScope(): void;
|
||||
|
||||
/**
|
||||
* Get the file that is related to the token
|
||||
*
|
||||
* @since 18.0.0
|
||||
* @return File
|
||||
*/
|
||||
public function getFile(): File;
|
||||
|
||||
/**
|
||||
* @since 18.0.0
|
||||
* @return string
|
||||
*/
|
||||
public function getEditor(): string;
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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 OCP\DirectEditing;
|
||||
|
||||
use OCP\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* @since 18.0.0
|
||||
*/
|
||||
class RegisterDirectEditorEvent extends Event {
|
||||
|
||||
/**
|
||||
* @var IManager
|
||||
*/
|
||||
private $manager;
|
||||
|
||||
/**
|
||||
* RegisterDirectEditorEvent constructor.
|
||||
*
|
||||
* @param IManager $manager
|
||||
* @since 18.0.0
|
||||
*/
|
||||
public function __construct(IManager $manager) {
|
||||
parent::__construct();
|
||||
$this->manager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 18.0.0
|
||||
* @param IEditor $editor
|
||||
*/
|
||||
public function register(IEditor $editor): void {
|
||||
$this->manager->registerDirectEditor($editor);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
namespace Test\DirectEditing;
|
||||
|
||||
use OC\DirectEditing\Manager;
|
||||
use OC\Files\Node\File;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\Http\NotFoundResponse;
|
||||
use OCP\AppFramework\Http\Response;
|
||||
use OCP\DirectEditing\ACreateEmpty;
|
||||
use OCP\DirectEditing\IEditor;
|
||||
use OCP\DirectEditing\IToken;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IUserSession;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class CreateEmpty extends ACreateEmpty {
|
||||
|
||||
public function getId(): string {
|
||||
return 'createEmpty';
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return 'create empty file';
|
||||
}
|
||||
|
||||
public function getExtension(): string {
|
||||
return '.txt';
|
||||
}
|
||||
|
||||
public function getMimetype(): string {
|
||||
return 'text/plain';
|
||||
}
|
||||
}
|
||||
|
||||
class Editor implements IEditor {
|
||||
|
||||
public function getId(): string {
|
||||
return 'testeditor';
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return 'Test editor';
|
||||
}
|
||||
|
||||
public function getMimetypes(): array {
|
||||
return [ 'text/plain' ];
|
||||
}
|
||||
|
||||
|
||||
public function getMimetypesOptional(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getCreators(): array {
|
||||
return [
|
||||
new CreateEmpty()
|
||||
];
|
||||
}
|
||||
|
||||
public function isSecure(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function open(IToken $token): Response {
|
||||
return new DataResponse('edit page');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class ManagerTest
|
||||
*
|
||||
* @package Test\DirectEditing
|
||||
* @group DB
|
||||
*/
|
||||
class ManagerTest extends TestCase {
|
||||
|
||||
private $manager;
|
||||
/**
|
||||
* @var Editor
|
||||
*/
|
||||
private $editor;
|
||||
/**
|
||||
* @var MockObject|ISecureRandom
|
||||
*/
|
||||
private $random;
|
||||
/**
|
||||
* @var IDBConnection
|
||||
*/
|
||||
private $connection;
|
||||
/**
|
||||
* @var MockObject|IUserSession
|
||||
*/
|
||||
private $userSession;
|
||||
/**
|
||||
* @var MockObject|IRootFolder
|
||||
*/
|
||||
private $rootFolder;
|
||||
/**
|
||||
* @var MockObject|Folder
|
||||
*/
|
||||
private $userFolder;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->editor = new Editor();
|
||||
|
||||
$this->random = $this->createMock(ISecureRandom::class);
|
||||
$this->connection = \OC::$server->getDatabaseConnection();
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->rootFolder = $this->createMock(IRootFolder::class);
|
||||
$this->userFolder = $this->createMock(Folder::class);
|
||||
|
||||
|
||||
$this->rootFolder->expects($this->any())
|
||||
->method('getUserFolder')
|
||||
->willReturn($this->userFolder);
|
||||
|
||||
$this->manager = new Manager(
|
||||
$this->random, $this->connection, $this->userSession, $this->rootFolder
|
||||
);
|
||||
|
||||
$this->manager->registerDirectEditor($this->editor);
|
||||
}
|
||||
|
||||
public function testEditorRegistration() {
|
||||
$this->assertEquals($this->manager->getEditors(), ['testeditor' => $this->editor]);
|
||||
}
|
||||
|
||||
|
||||
public function testCreateToken() {
|
||||
$expectedToken = 'TOKEN' . time();
|
||||
$file = $this->createMock(File::class);
|
||||
$file->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(123);
|
||||
$this->random->expects($this->once())
|
||||
->method('generate')
|
||||
->willReturn($expectedToken);
|
||||
$this->userFolder->expects($this->once())
|
||||
->method('newFile')
|
||||
->willReturn($file);
|
||||
$token = $this->manager->create('/File.txt', 'testeditor', 'createEmpty');
|
||||
$this->assertEquals($token, $expectedToken);
|
||||
}
|
||||
|
||||
public function testCreateTokenAccess() {
|
||||
$expectedToken = 'TOKEN' . time();
|
||||
$file = $this->createMock(File::class);
|
||||
$file->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(123);
|
||||
$this->random->expects($this->once())
|
||||
->method('generate')
|
||||
->willReturn($expectedToken);
|
||||
$this->userFolder->expects($this->once())
|
||||
->method('newFile')
|
||||
->willReturn($file);
|
||||
$this->manager->create('/File.txt', 'testeditor', 'createEmpty');
|
||||
$firstResult = $this->manager->edit($expectedToken);
|
||||
$secondResult = $this->manager->edit($expectedToken);
|
||||
$this->assertInstanceOf(DataResponse::class, $firstResult);
|
||||
$this->assertInstanceOf(NotFoundResponse::class, $secondResult);
|
||||
}
|
||||
|
||||
}
|
|
@ -29,7 +29,7 @@
|
|||
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
|
||||
// when updating major/minor version number.
|
||||
|
||||
$OC_Version = array(18, 0, 0, 1);
|
||||
$OC_Version = array(18, 0, 0, 2);
|
||||
|
||||
// The human readable string
|
||||
$OC_VersionString = '18.0.0 Alpha';
|
||||
|
|
Loading…
Reference in New Issue