Merge pull request #9353 from nextcloud/feature/4724/versions_dav
Add versions DAV endpoint
This commit is contained in:
commit
dc5c4b2e1f
|
@ -8,7 +8,7 @@
|
|||
This application automatically maintains older versions of files that are changed. When enabled, a hidden versions folder is provisioned in every user’s directory and is used to store old file versions. A user can revert to an older version through the web interface at any time, with the replaced file becoming a version. The app automatically manages the versions folder to ensure the user doesn’t run out of Quota because of versions.
|
||||
In addition to the expiry of versions, the versions app makes certain never to use more than 50% of the user’s currently available free space. If stored versions exceed this limit, the app will delete the oldest versions first until it meets this limit. More information is available in the Versions documentation.
|
||||
</description>
|
||||
<version>1.7.0</version>
|
||||
<version>1.7.1</version>
|
||||
<licence>agpl</licence>
|
||||
<author>Frank Karlitschek</author>
|
||||
<author>Bjoern Schiessle</author>
|
||||
|
@ -16,6 +16,7 @@
|
|||
<default_enable/>
|
||||
<types>
|
||||
<filesystem/>
|
||||
<dav/>
|
||||
</types>
|
||||
<documentation>
|
||||
<user>user-versions</user>
|
||||
|
@ -34,4 +35,10 @@
|
|||
<command>OCA\Files_Versions\Command\CleanUp</command>
|
||||
<command>OCA\Files_Versions\Command\ExpireVersions</command>
|
||||
</commands>
|
||||
|
||||
<sabre>
|
||||
<collections>
|
||||
<collection>OCA\Files_Versions\Sabre\RootCollection</collection>
|
||||
</collections>
|
||||
</sabre>
|
||||
</info>
|
||||
|
|
|
@ -16,5 +16,11 @@ return array(
|
|||
'OCA\\Files_Versions\\Events\\CreateVersionEvent' => $baseDir . '/../lib/Events/CreateVersionEvent.php',
|
||||
'OCA\\Files_Versions\\Expiration' => $baseDir . '/../lib/Expiration.php',
|
||||
'OCA\\Files_Versions\\Hooks' => $baseDir . '/../lib/Hooks.php',
|
||||
'OCA\\Files_Versions\\Sabre\\RestoreFolder' => $baseDir . '/../lib/Sabre/RestoreFolder.php',
|
||||
'OCA\\Files_Versions\\Sabre\\RootCollection' => $baseDir . '/../lib/Sabre/RootCollection.php',
|
||||
'OCA\\Files_Versions\\Sabre\\VersionCollection' => $baseDir . '/../lib/Sabre/VersionCollection.php',
|
||||
'OCA\\Files_Versions\\Sabre\\VersionFile' => $baseDir . '/../lib/Sabre/VersionFile.php',
|
||||
'OCA\\Files_Versions\\Sabre\\VersionHome' => $baseDir . '/../lib/Sabre/VersionHome.php',
|
||||
'OCA\\Files_Versions\\Sabre\\VersionRoot' => $baseDir . '/../lib/Sabre/VersionRoot.php',
|
||||
'OCA\\Files_Versions\\Storage' => $baseDir . '/../lib/Storage.php',
|
||||
);
|
||||
|
|
|
@ -31,6 +31,12 @@ class ComposerStaticInitFiles_Versions
|
|||
'OCA\\Files_Versions\\Events\\CreateVersionEvent' => __DIR__ . '/..' . '/../lib/Events/CreateVersionEvent.php',
|
||||
'OCA\\Files_Versions\\Expiration' => __DIR__ . '/..' . '/../lib/Expiration.php',
|
||||
'OCA\\Files_Versions\\Hooks' => __DIR__ . '/..' . '/../lib/Hooks.php',
|
||||
'OCA\\Files_Versions\\Sabre\\RestoreFolder' => __DIR__ . '/..' . '/../lib/Sabre/RestoreFolder.php',
|
||||
'OCA\\Files_Versions\\Sabre\\RootCollection' => __DIR__ . '/..' . '/../lib/Sabre/RootCollection.php',
|
||||
'OCA\\Files_Versions\\Sabre\\VersionCollection' => __DIR__ . '/..' . '/../lib/Sabre/VersionCollection.php',
|
||||
'OCA\\Files_Versions\\Sabre\\VersionFile' => __DIR__ . '/..' . '/../lib/Sabre/VersionFile.php',
|
||||
'OCA\\Files_Versions\\Sabre\\VersionHome' => __DIR__ . '/..' . '/../lib/Sabre/VersionHome.php',
|
||||
'OCA\\Files_Versions\\Sabre\\VersionRoot' => __DIR__ . '/..' . '/../lib/Sabre/VersionRoot.php',
|
||||
'OCA\\Files_Versions\\Storage' => __DIR__ . '/..' . '/../lib/Storage.php',
|
||||
);
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
namespace OCA\Files_Versions\AppInfo;
|
||||
|
||||
use OCA\DAV\Connector\Sabre\Principal;
|
||||
use OCP\AppFramework\App;
|
||||
use OCA\Files_Versions\Expiration;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
|
@ -48,5 +49,17 @@ class Application extends App {
|
|||
$c->query(ITimeFactory::class)
|
||||
);
|
||||
});
|
||||
|
||||
/*
|
||||
* Register $principalBackend for the DAV collection
|
||||
*/
|
||||
$container->registerService('principalBackend', function () {
|
||||
return new Principal(
|
||||
\OC::$server->getUserManager(),
|
||||
\OC::$server->getGroupManager(),
|
||||
\OC::$server->getShareManager(),
|
||||
\OC::$server->getUserSession()
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @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_Versions\Sabre;
|
||||
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\ICollection;
|
||||
use Sabre\DAV\IMoveTarget;
|
||||
use Sabre\DAV\INode;
|
||||
|
||||
|
||||
class RestoreFolder implements ICollection, IMoveTarget {
|
||||
|
||||
/** @var string */
|
||||
protected $userId;
|
||||
|
||||
public function __construct(string $userId) {
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
public function createFile($name, $data = null) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function createDirectory($name) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function getChild($name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
return 'restore';
|
||||
}
|
||||
|
||||
public function setName($name) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function getLastModified(): int {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getChildren(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function childExists($name): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function moveInto($targetName, $sourcePath, INode $sourceNode): bool {
|
||||
if (!($sourceNode instanceof VersionFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $sourceNode->rollBack();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @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_Versions\Sabre;
|
||||
|
||||
use OCP\Files\IRootFolder;
|
||||
use Sabre\DAV\INode;
|
||||
use Sabre\DAVACL\AbstractPrincipalCollection;
|
||||
use Sabre\DAVACL\PrincipalBackend;
|
||||
|
||||
class RootCollection extends AbstractPrincipalCollection {
|
||||
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
public function __construct(PrincipalBackend\BackendInterface $principalBackend,
|
||||
IRootFolder $rootFolder) {
|
||||
parent::__construct($principalBackend, 'principals/users');
|
||||
|
||||
$this->rootFolder = $rootFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a node for a principal.
|
||||
*
|
||||
* The passed array contains principal information, and is guaranteed to
|
||||
* at least contain a uri item. Other properties may or may not be
|
||||
* supplied by the authentication backend.
|
||||
*
|
||||
* @param array $principalInfo
|
||||
* @return INode
|
||||
*/
|
||||
public function getChildForPrincipal(array $principalInfo) {
|
||||
list(,$name) = \Sabre\Uri\split($principalInfo['uri']);
|
||||
$user = \OC::$server->getUserSession()->getUser();
|
||||
if (is_null($user) || $name !== $user->getUID()) {
|
||||
throw new \Sabre\DAV\Exception\Forbidden();
|
||||
}
|
||||
return new VersionHome($principalInfo, $this->rootFolder);
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
return 'versions';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @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_Versions\Sabre;
|
||||
|
||||
use OCA\Files_Versions\Storage;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\Folder;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\Exception\NotFound;
|
||||
use Sabre\DAV\ICollection;
|
||||
|
||||
class VersionCollection implements ICollection {
|
||||
/** @var Folder */
|
||||
private $userFolder;
|
||||
|
||||
/** @var File */
|
||||
private $file;
|
||||
|
||||
/** @var string */
|
||||
private $userId;
|
||||
|
||||
public function __construct(Folder $userFolder, File $file, string $userId) {
|
||||
$this->userFolder = $userFolder;
|
||||
$this->file = $file;
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
public function createFile($name, $data = null) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function createDirectory($name) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function getChild($name) {
|
||||
/** @var VersionFile[] $versions */
|
||||
$versions = $this->getChildren();
|
||||
|
||||
foreach ($versions as $version) {
|
||||
if ($version->getName() === $name) {
|
||||
return $version;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
public function getChildren(): array {
|
||||
$versions = Storage::getVersions($this->userId, $this->userFolder->getRelativePath($this->file->getPath()));
|
||||
|
||||
return array_map(function (array $data) {
|
||||
return new VersionFile($data, $this->userFolder->getParent());
|
||||
}, $versions);
|
||||
}
|
||||
|
||||
public function childExists($name): bool {
|
||||
try {
|
||||
$this->getChild($name);
|
||||
return true;
|
||||
} catch (NotFound $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return (string)$this->file->getId();
|
||||
}
|
||||
|
||||
public function setName($name) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function getLastModified(): int {
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @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_Versions\Sabre;
|
||||
|
||||
use OCA\Files_Versions\Storage;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\NotFoundException;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\Exception\NotFound;
|
||||
use Sabre\DAV\IFile;
|
||||
|
||||
class VersionFile implements IFile {
|
||||
/** @var array */
|
||||
private $data;
|
||||
|
||||
/** @var Folder */
|
||||
private $userRoot;
|
||||
|
||||
public function __construct(array $data, Folder $userRoot) {
|
||||
$this->data = $data;
|
||||
$this->userRoot = $userRoot;
|
||||
}
|
||||
|
||||
public function put($data) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function get() {
|
||||
try {
|
||||
/** @var Folder $versions */
|
||||
$versions = $this->userRoot->get('files_versions');
|
||||
/** @var File $version */
|
||||
$version = $versions->get($this->data['path'].'.v'.$this->data['version']);
|
||||
} catch (NotFoundException $e) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
return $version->fopen('rb');
|
||||
}
|
||||
|
||||
public function getContentType(): string {
|
||||
return $this->data['mimetype'];
|
||||
}
|
||||
|
||||
public function getETag(): string {
|
||||
return $this->data['version'];
|
||||
}
|
||||
|
||||
public function getSize(): int {
|
||||
return $this->data['size'];
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->data['version'];
|
||||
}
|
||||
|
||||
public function setName($name) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function getLastModified(): int {
|
||||
return (int)$this->data['version'];
|
||||
}
|
||||
|
||||
public function rollBack(): bool {
|
||||
return Storage::rollback($this->data['path'], $this->data['version']);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @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_Versions\Sabre;
|
||||
|
||||
use OCP\Files\IRootFolder;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\ICollection;
|
||||
|
||||
class VersionHome implements ICollection {
|
||||
|
||||
/** @var array */
|
||||
private $principalInfo;
|
||||
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
public function __construct(array $principalInfo, IRootFolder $rootFolder) {
|
||||
$this->principalInfo = $principalInfo;
|
||||
$this->rootFolder = $rootFolder;
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
list(,$name) = \Sabre\Uri\split($this->principalInfo['uri']);
|
||||
return $name;
|
||||
}
|
||||
|
||||
public function setName($name) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function createFile($name, $data = null) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function createDirectory($name) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function getChild($name) {
|
||||
list(,$userId) = \Sabre\Uri\split($this->principalInfo['uri']);
|
||||
|
||||
if ($name === 'versions') {
|
||||
return new VersionRoot($userId, $this->rootFolder);
|
||||
}
|
||||
if ($name === 'restore') {
|
||||
return new RestoreFolder($userId);
|
||||
}
|
||||
}
|
||||
|
||||
public function getChildren() {
|
||||
list(,$userId) = \Sabre\Uri\split($this->principalInfo['uri']);
|
||||
|
||||
return [
|
||||
new VersionRoot($userId, $this->rootFolder),
|
||||
new RestoreFolder($userId),
|
||||
];
|
||||
}
|
||||
|
||||
public function childExists($name) {
|
||||
return $name === 'versions' || $name === 'restore';
|
||||
}
|
||||
|
||||
public function getLastModified() {
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @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_Versions\Sabre;
|
||||
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\IRootFolder;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\Exception\NotFound;
|
||||
use Sabre\DAV\ICollection;
|
||||
|
||||
class VersionRoot implements ICollection {
|
||||
|
||||
/** @var string */
|
||||
private $userId;
|
||||
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
public function __construct(string $userId, IRootFolder $rootFolder) {
|
||||
$this->userId = $userId;
|
||||
$this->rootFolder = $rootFolder;
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return 'versions';
|
||||
}
|
||||
|
||||
public function setName($name) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function createFile($name, $data = null) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function createDirectory($name) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function getChild($name) {
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->userId);
|
||||
|
||||
$fileId = (int)$name;
|
||||
$nodes = $userFolder->getById($fileId);
|
||||
|
||||
if ($nodes === []) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$node = array_pop($nodes);
|
||||
|
||||
if (!$node instanceof File) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
return new VersionCollection($userFolder, $node, $this->userId);
|
||||
}
|
||||
|
||||
public function getChildren(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function childExists($name): bool {
|
||||
try {
|
||||
$this->getChild($name);
|
||||
return true;
|
||||
} catch (NotFound $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getLastModified(): int {
|
||||
return 0;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue