aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/dav/lib/Connector/Sabre/File.php27
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/FileTest.php6
-rw-r--r--apps/files/css/files.scss15
-rw-r--r--apps/files/js/detailsview.js3
-rw-r--r--apps/files/js/detailtabview.js15
-rw-r--r--apps/files/l10n/cs.js1
-rw-r--r--apps/files/l10n/cs.json1
-rw-r--r--apps/files_versions/appinfo/info.xml4
-rw-r--r--apps/files_versions/composer/composer/autoload_classmap.php7
-rw-r--r--apps/files_versions/composer/composer/autoload_static.php7
-rw-r--r--apps/files_versions/lib/AppInfo/Application.php48
-rw-r--r--apps/files_versions/lib/Controller/PreviewController.php47
-rw-r--r--apps/files_versions/lib/Sabre/RestoreFolder.php12
-rw-r--r--apps/files_versions/lib/Sabre/RootCollection.php25
-rw-r--r--apps/files_versions/lib/Sabre/VersionCollection.php22
-rw-r--r--apps/files_versions/lib/Sabre/VersionFile.php41
-rw-r--r--apps/files_versions/lib/Sabre/VersionHome.php37
-rw-r--r--apps/files_versions/lib/Sabre/VersionRoot.php18
-rw-r--r--apps/files_versions/lib/Storage.php25
-rw-r--r--apps/files_versions/lib/Versions/BackendNotFoundException.php26
-rw-r--r--apps/files_versions/lib/Versions/IVersion.php99
-rw-r--r--apps/files_versions/lib/Versions/IVersionBackend.php81
-rw-r--r--apps/files_versions/lib/Versions/IVersionManager.php36
-rw-r--r--apps/files_versions/lib/Versions/LegacyVersionsBackend.php105
-rw-r--r--apps/files_versions/lib/Versions/Version.php113
-rw-r--r--apps/files_versions/lib/Versions/VersionManager.php93
-rw-r--r--apps/files_versions/tests/Controller/PreviewControllerTest.php55
-rw-r--r--core/css/global.scss6
-rw-r--r--core/css/header.scss1
-rw-r--r--core/l10n/cs.js1
-rw-r--r--core/l10n/cs.json1
-rw-r--r--core/l10n/de.js3
-rw-r--r--core/l10n/de.json3
-rw-r--r--core/l10n/de_DE.js3
-rw-r--r--core/l10n/de_DE.json3
-rw-r--r--core/l10n/it.js3
-rw-r--r--core/l10n/it.json3
-rw-r--r--core/l10n/pt_BR.js3
-rw-r--r--core/l10n/pt_BR.json3
-rw-r--r--core/l10n/tr.js3
-rw-r--r--core/l10n/tr.json3
-rw-r--r--lib/composer/composer/autoload_classmap.php2
-rw-r--r--lib/composer/composer/autoload_static.php2
-rw-r--r--lib/private/Files/ObjectStore/ObjectStoreStorage.php68
-rw-r--r--lib/private/Files/Storage/Common.php22
-rw-r--r--lib/private/Files/Storage/Local.php4
-rw-r--r--lib/private/Files/Storage/Wrapper/Encryption.php9
-rw-r--r--lib/private/Files/Storage/Wrapper/Jail.php15
-rw-r--r--lib/private/Files/Storage/Wrapper/Wrapper.php17
-rw-r--r--lib/private/Files/Stream/CountReadStream.php65
-rw-r--r--lib/private/FullTextSearch/FullTextSearchManager.php2
-rw-r--r--lib/private/Preview/Generator.php18
-rw-r--r--lib/public/Files/Storage/IWriteStreamStorage.php40
-rw-r--r--lib/public/FullTextSearch/Service/IIndexService.php4
-rw-r--r--resources/app-info.xsd17
-rw-r--r--settings/BackgroundJobs/VerifyUserData.php4
-rw-r--r--tests/lib/Files/Storage/Storage.php17
-rw-r--r--tests/lib/Files/Stream/CountReadStreamTest.php49
-rw-r--r--tests/lib/Preview/GeneratorTest.php22
59 files changed, 1204 insertions, 181 deletions
diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php
index 9e927ff85e5..57c072fda47 100644
--- a/apps/dav/lib/Connector/Sabre/File.php
+++ b/apps/dav/lib/Connector/Sabre/File.php
@@ -164,14 +164,19 @@ class File extends Node implements IFile {
$this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
}
- $target = $partStorage->fopen($internalPartPath, 'wb');
- if ($target === false) {
- \OC::$server->getLogger()->error('\OC\Files\Filesystem::fopen() failed', ['app' => 'webdav']);
- // because we have no clue about the cause we can only throw back a 500/Internal Server Error
- throw new Exception('Could not write file contents');
+ if ($partStorage->instanceOfStorage(Storage\IWriteStreamStorage::class)) {
+ $count = $partStorage->writeStream($internalPartPath, $data);
+ $result = $count > 0;
+ } else {
+ $target = $partStorage->fopen($internalPartPath, 'wb');
+ if ($target === false) {
+ \OC::$server->getLogger()->error('\OC\Files\Filesystem::fopen() failed', ['app' => 'webdav']);
+ // because we have no clue about the cause we can only throw back a 500/Internal Server Error
+ throw new Exception('Could not write file contents');
+ }
+ list($count, $result) = \OC_Helper::streamCopy($data, $target);
+ fclose($target);
}
- list($count, $result) = \OC_Helper::streamCopy($data, $target);
- fclose($target);
if ($result === false) {
$expected = -1;
@@ -185,7 +190,7 @@ class File extends Node implements IFile {
// double check if the file was fully received
// compare expected and actual size
if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
- $expected = (int) $_SERVER['CONTENT_LENGTH'];
+ $expected = (int)$_SERVER['CONTENT_LENGTH'];
if ($count !== $expected) {
throw new BadRequest('expected filesize ' . $expected . ' got ' . $count);
}
@@ -219,7 +224,7 @@ class File extends Node implements IFile {
$renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath);
$fileExists = $storage->file_exists($internalPath);
if ($renameOkay === false || $fileExists === false) {
- \OC::$server->getLogger()->error('renaming part file to final file failed ($run: ' . ( $run ? 'true' : 'false' ) . ', $renameOkay: ' . ( $renameOkay ? 'true' : 'false' ) . ', $fileExists: ' . ( $fileExists ? 'true' : 'false' ) . ')', ['app' => 'webdav']);
+ \OC::$server->getLogger()->error('renaming part file to final file failed $renameOkay: ' . ($renameOkay ? 'true' : 'false') . ', $fileExists: ' . ($fileExists ? 'true' : 'false') . ')', ['app' => 'webdav']);
throw new Exception('Could not rename part file to final file');
}
} catch (ForbiddenException $ex) {
@@ -246,7 +251,7 @@ class File extends Node implements IFile {
$this->header('X-OC-MTime: accepted');
}
}
-
+
if ($view) {
$this->emitPostHooks($exists);
}
@@ -443,7 +448,7 @@ class File extends Node implements IFile {
//detect aborted upload
if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
if (isset($_SERVER['CONTENT_LENGTH'])) {
- $expected = (int) $_SERVER['CONTENT_LENGTH'];
+ $expected = (int)$_SERVER['CONTENT_LENGTH'];
if ($bytesWritten !== $expected) {
$chunk_handler->remove($info['index']);
throw new BadRequest(
diff --git a/apps/dav/tests/unit/Connector/Sabre/FileTest.php b/apps/dav/tests/unit/Connector/Sabre/FileTest.php
index 5e7a6374206..edb61edc6ed 100644
--- a/apps/dav/tests/unit/Connector/Sabre/FileTest.php
+++ b/apps/dav/tests/unit/Connector/Sabre/FileTest.php
@@ -164,7 +164,7 @@ class FileTest extends \Test\TestCase {
public function testSimplePutFails($thrownException, $expectedException, $checkPreviousClass = true) {
// setup
$storage = $this->getMockBuilder(Local::class)
- ->setMethods(['fopen'])
+ ->setMethods(['writeStream'])
->setConstructorArgs([['datadir' => \OC::$server->getTempManager()->getTemporaryFolder()]])
->getMock();
\OC\Files\Filesystem::mount($storage, [], $this->user . '/');
@@ -182,11 +182,11 @@ class FileTest extends \Test\TestCase {
if ($thrownException !== null) {
$storage->expects($this->once())
- ->method('fopen')
+ ->method('writeStream')
->will($this->throwException($thrownException));
} else {
$storage->expects($this->once())
- ->method('fopen')
+ ->method('writeStream')
->will($this->returnValue(false));
}
diff --git a/apps/files/css/files.scss b/apps/files/css/files.scss
index 580bab32d6d..d6f9bd6131e 100644
--- a/apps/files/css/files.scss
+++ b/apps/files/css/files.scss
@@ -8,7 +8,12 @@
}
/* FILE MENU */
-.actions { padding:5px; height:32px; display: inline-block; float: left; }
+.actions {
+ padding: 5px;
+ height: 100%;
+ display: inline-block;
+ float: left;
+}
.actions input, .actions button, .actions .button { margin:0; float:left; }
.actions .button a { color: #555; }
.actions .button a:hover,
@@ -659,8 +664,14 @@ table.dragshadow td.size {
top: 100%;
margin-top: 4px;
min-width: 100px;
- margin-left: 7px;
+ margin-left: 22px; /* Align left edge below center of + button … */
+ transform: translateX(-50%); /* … then center it below button */
z-index: 1001;
+
+ /* Center triangle */
+ &::after {
+ left: calc(50% - 8px) !important;
+ }
}
#filestable .filename .action .icon,
diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js
index cd602961c0a..bac2a5ebd21 100644
--- a/apps/files/js/detailsview.js
+++ b/apps/files/js/detailsview.js
@@ -174,6 +174,9 @@
// hide other tabs
$tabsContainer.find('.tab').addClass('hidden');
+ $tabsContainer.attr('class', 'tabsContainer');
+ $tabsContainer.addClass(tabView.getTabsContainerExtraClasses());
+
// tab already rendered ?
if (!$tabEl.length) {
// render tab
diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js
index a66cedbc15d..1e046f30246 100644
--- a/apps/files/js/detailtabview.js
+++ b/apps/files/js/detailtabview.js
@@ -41,6 +41,21 @@
},
/**
+ * Returns the extra CSS classes used by the tabs container when this
+ * tab is the selected one.
+ *
+ * In general you should not extend this method, as tabs should not
+ * modify the classes of its container; this is reserved as a last
+ * resort for very specific cases in which there is no other way to get
+ * the proper style or behaviour.
+ *
+ * @return {String} space-separated CSS classes
+ */
+ getTabsContainerExtraClasses: function() {
+ return '';
+ },
+
+ /**
* Returns the tab label
*
* @return {String} label
diff --git a/apps/files/l10n/cs.js b/apps/files/l10n/cs.js
index cbd6c72d572..96b93d361e7 100644
--- a/apps/files/l10n/cs.js
+++ b/apps/files/l10n/cs.js
@@ -142,6 +142,7 @@ OC.L10N.register(
"WebDAV" : "WebDAV",
"Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener\">access your Files via WebDAV</a>" : "Použijte tuto adresu pro <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener\">přístup k vašim souborům přes WebDAV</a>",
"Cancel upload" : "Zrušit nahrávání",
+ "Toggle grid view" : "Přepnout zobrazení mřížky",
"No files in here" : "Žádné soubory",
"Upload some content or sync with your devices!" : "Nahrajte nějaký obsah nebo synchronizujte se svými přístroji!",
"No entries found in this folder" : "V této složce nebylo nic nalezeno",
diff --git a/apps/files/l10n/cs.json b/apps/files/l10n/cs.json
index a8f82c5c5d7..cb01165503d 100644
--- a/apps/files/l10n/cs.json
+++ b/apps/files/l10n/cs.json
@@ -140,6 +140,7 @@
"WebDAV" : "WebDAV",
"Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener\">access your Files via WebDAV</a>" : "Použijte tuto adresu pro <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener\">přístup k vašim souborům přes WebDAV</a>",
"Cancel upload" : "Zrušit nahrávání",
+ "Toggle grid view" : "Přepnout zobrazení mřížky",
"No files in here" : "Žádné soubory",
"Upload some content or sync with your devices!" : "Nahrajte nějaký obsah nebo synchronizujte se svými přístroji!",
"No entries found in this folder" : "V této složce nebylo nic nalezeno",
diff --git a/apps/files_versions/appinfo/info.xml b/apps/files_versions/appinfo/info.xml
index d2f873edb07..6d1b3085f80 100644
--- a/apps/files_versions/appinfo/info.xml
+++ b/apps/files_versions/appinfo/info.xml
@@ -41,4 +41,8 @@
<collection>OCA\Files_Versions\Sabre\RootCollection</collection>
</collections>
</sabre>
+
+ <versions>
+ <backend for="OCP\Files\Storage\IStorage">OCA\Files_Versions\Versions\LegacyVersionsBackend</backend>
+ </versions>
</info>
diff --git a/apps/files_versions/composer/composer/autoload_classmap.php b/apps/files_versions/composer/composer/autoload_classmap.php
index 4bb112b4f11..1283e533914 100644
--- a/apps/files_versions/composer/composer/autoload_classmap.php
+++ b/apps/files_versions/composer/composer/autoload_classmap.php
@@ -23,4 +23,11 @@ return array(
'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',
+ 'OCA\\Files_Versions\\Versions\\BackendNotFoundException' => $baseDir . '/../lib/Versions/BackendNotFoundException.php',
+ 'OCA\\Files_Versions\\Versions\\IVersion' => $baseDir . '/../lib/Versions/IVersion.php',
+ 'OCA\\Files_Versions\\Versions\\IVersionBackend' => $baseDir . '/../lib/Versions/IVersionBackend.php',
+ 'OCA\\Files_Versions\\Versions\\IVersionManager' => $baseDir . '/../lib/Versions/IVersionManager.php',
+ 'OCA\\Files_Versions\\Versions\\LegacyVersionsBackend' => $baseDir . '/../lib/Versions/LegacyVersionsBackend.php',
+ 'OCA\\Files_Versions\\Versions\\Version' => $baseDir . '/../lib/Versions/Version.php',
+ 'OCA\\Files_Versions\\Versions\\VersionManager' => $baseDir . '/../lib/Versions/VersionManager.php',
);
diff --git a/apps/files_versions/composer/composer/autoload_static.php b/apps/files_versions/composer/composer/autoload_static.php
index 29bc592b41c..6a6b753c2e5 100644
--- a/apps/files_versions/composer/composer/autoload_static.php
+++ b/apps/files_versions/composer/composer/autoload_static.php
@@ -38,6 +38,13 @@ class ComposerStaticInitFiles_Versions
'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',
+ 'OCA\\Files_Versions\\Versions\\BackendNotFoundException' => __DIR__ . '/..' . '/../lib/Versions/BackendNotFoundException.php',
+ 'OCA\\Files_Versions\\Versions\\IVersion' => __DIR__ . '/..' . '/../lib/Versions/IVersion.php',
+ 'OCA\\Files_Versions\\Versions\\IVersionBackend' => __DIR__ . '/..' . '/../lib/Versions/IVersionBackend.php',
+ 'OCA\\Files_Versions\\Versions\\IVersionManager' => __DIR__ . '/..' . '/../lib/Versions/IVersionManager.php',
+ 'OCA\\Files_Versions\\Versions\\LegacyVersionsBackend' => __DIR__ . '/..' . '/../lib/Versions/LegacyVersionsBackend.php',
+ 'OCA\\Files_Versions\\Versions\\Version' => __DIR__ . '/..' . '/../lib/Versions/Version.php',
+ 'OCA\\Files_Versions\\Versions\\VersionManager' => __DIR__ . '/..' . '/../lib/Versions/VersionManager.php',
);
public static function getInitializer(ClassLoader $loader)
diff --git a/apps/files_versions/lib/AppInfo/Application.php b/apps/files_versions/lib/AppInfo/Application.php
index 340b5ab5cbd..935556221fa 100644
--- a/apps/files_versions/lib/AppInfo/Application.php
+++ b/apps/files_versions/lib/AppInfo/Application.php
@@ -24,9 +24,10 @@
namespace OCA\Files_Versions\AppInfo;
use OCA\DAV\Connector\Sabre\Principal;
+use OCA\Files_Versions\Versions\IVersionManager;
+use OCA\Files_Versions\Versions\VersionManager;
use OCP\AppFramework\App;
-use OCA\Files_Versions\Expiration;
-use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\AppFramework\IAppContainer;
use OCA\Files_Versions\Capabilities;
class Application extends App {
@@ -43,14 +44,45 @@ class Application extends App {
/*
* Register $principalBackend for the DAV collection
*/
- $container->registerService('principalBackend', function () {
+ $container->registerService('principalBackend', function (IAppContainer $c) {
+ $server = $c->getServer();
return new Principal(
- \OC::$server->getUserManager(),
- \OC::$server->getGroupManager(),
- \OC::$server->getShareManager(),
- \OC::$server->getUserSession(),
- \OC::$server->getConfig()
+ $server->getUserManager(),
+ $server->getGroupManager(),
+ $server->getShareManager(),
+ $server->getUserSession(),
+ $server->getConfig()
);
});
+
+ $container->registerService(IVersionManager::class, function(IAppContainer $c) {
+ return new VersionManager();
+ });
+
+ $this->registerVersionBackends();
+ }
+
+ public function registerVersionBackends() {
+ $server = $this->getContainer()->getServer();
+ $logger = $server->getLogger();
+ $appManager = $server->getAppManager();
+ /** @var IVersionManager $versionManager */
+ $versionManager = $this->getContainer()->getServer()->query(IVersionManager::class);
+ foreach($appManager->getInstalledApps() as $app) {
+ $appInfo = $appManager->getAppInfo($app);
+ if (isset($appInfo['versions'])) {
+ $backends = $appInfo['versions'];
+ foreach($backends as $backend) {
+ $class = $backend['@value'];
+ $for = $backend['@attributes']['for'];
+ try {
+ $backendObject = $server->query($class);
+ $versionManager->registerBackend($for, $backendObject);
+ } catch (\Exception $e) {
+ $logger->logException($e);
+ }
+ }
+ }
+ }
}
}
diff --git a/apps/files_versions/lib/Controller/PreviewController.php b/apps/files_versions/lib/Controller/PreviewController.php
index b8bf464fb3f..f41250a8971 100644
--- a/apps/files_versions/lib/Controller/PreviewController.php
+++ b/apps/files_versions/lib/Controller/PreviewController.php
@@ -21,45 +21,53 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+
namespace OCA\Files_Versions\Controller;
+use OCA\Files_Versions\Versions\IVersionManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
-use OCP\Files\File;
-use OCP\Files\Folder;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\IPreview;
use OCP\IRequest;
+use OCP\IUserSession;
class PreviewController extends Controller {
/** @var IRootFolder */
private $rootFolder;
- /** @var string */
- private $userId;
+ /** @var IUserSession */
+ private $userSession;
/** @var IMimeTypeDetector */
private $mimeTypeDetector;
+ /** @var IVersionManager */
+ private $versionManager;
+
/** @var IPreview */
private $previewManager;
- public function __construct($appName,
- IRequest $request,
- IRootFolder $rootFolder,
- $userId,
- IMimeTypeDetector $mimeTypeDetector,
- IPreview $previewManager) {
+ public function __construct(
+ $appName,
+ IRequest $request,
+ IRootFolder $rootFolder,
+ IUserSession $userSession,
+ IMimeTypeDetector $mimeTypeDetector,
+ IVersionManager $versionManager,
+ IPreview $previewManager
+ ) {
parent::__construct($appName, $request);
$this->rootFolder = $rootFolder;
- $this->userId = $userId;
+ $this->userSession = $userSession;
$this->mimeTypeDetector = $mimeTypeDetector;
+ $this->versionManager = $versionManager;
$this->previewManager = $previewManager;
}
@@ -79,20 +87,17 @@ class PreviewController extends Controller {
$y = 44,
$version = ''
) {
- if($file === '' || $version === '' || $x === 0 || $y === 0) {
+ if ($file === '' || $version === '' || $x === 0 || $y === 0) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
try {
- $userFolder = $this->rootFolder->getUserFolder($this->userId);
- /** @var Folder $versionFolder */
- $versionFolder = $userFolder->getParent()->get('files_versions');
- $mimeType = $this->mimeTypeDetector->detectPath($file);
- $file = $versionFolder->get($file.'.v'.$version);
-
- /** @var File $file */
- $f = $this->previewManager->getPreview($file, $x, $y, true, IPreview::MODE_FILL, $mimeType);
- return new FileDisplayResponse($f, Http::STATUS_OK, ['Content-Type' => $f->getMimeType()]);
+ $user = $this->userSession->getUser();
+ $userFolder = $this->rootFolder->getUserFolder($user->getUID());
+ $file = $userFolder->get($file);
+ $versionFile = $this->versionManager->getVersionFile($user, $file, (int)$version);
+ $preview = $this->previewManager->getPreview($versionFile, $x, $y, true, IPreview::MODE_FILL, $versionFile->getMimetype());
+ return new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => $preview->getMimeType()]);
} catch (NotFoundException $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
} catch (\InvalidArgumentException $e) {
diff --git a/apps/files_versions/lib/Sabre/RestoreFolder.php b/apps/files_versions/lib/Sabre/RestoreFolder.php
index c398d02692b..c8504646bad 100644
--- a/apps/files_versions/lib/Sabre/RestoreFolder.php
+++ b/apps/files_versions/lib/Sabre/RestoreFolder.php
@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace OCA\Files_Versions\Sabre;
+use OCP\IUser;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\ICollection;
use Sabre\DAV\IMoveTarget;
@@ -31,14 +32,6 @@ 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();
}
@@ -80,7 +73,8 @@ class RestoreFolder implements ICollection, IMoveTarget {
return false;
}
- return $sourceNode->rollBack();
+ $sourceNode->rollBack();
+ return true;
}
}
diff --git a/apps/files_versions/lib/Sabre/RootCollection.php b/apps/files_versions/lib/Sabre/RootCollection.php
index ca5979573b5..504c3362505 100644
--- a/apps/files_versions/lib/Sabre/RootCollection.php
+++ b/apps/files_versions/lib/Sabre/RootCollection.php
@@ -20,10 +20,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+
namespace OCA\Files_Versions\Sabre;
+use OCA\Files_Versions\Versions\IVersionManager;
use OCP\Files\IRootFolder;
use OCP\IConfig;
+use OCP\IUserManager;
use Sabre\DAV\INode;
use Sabre\DAVACL\AbstractPrincipalCollection;
use Sabre\DAVACL\PrincipalBackend;
@@ -33,12 +36,24 @@ class RootCollection extends AbstractPrincipalCollection {
/** @var IRootFolder */
private $rootFolder;
- public function __construct(PrincipalBackend\BackendInterface $principalBackend,
- IRootFolder $rootFolder,
- IConfig $config) {
+ /** @var IUserManager */
+ private $userManager;
+
+ /** @var IVersionManager */
+ private $versionManager;
+
+ public function __construct(
+ PrincipalBackend\BackendInterface $principalBackend,
+ IRootFolder $rootFolder,
+ IConfig $config,
+ IUserManager $userManager,
+ IVersionManager $versionManager
+ ) {
parent::__construct($principalBackend, 'principals/users');
$this->rootFolder = $rootFolder;
+ $this->userManager = $userManager;
+ $this->versionManager = $versionManager;
$this->disableListing = !$config->getSystemValue('debug', false);
}
@@ -54,12 +69,12 @@ class RootCollection extends AbstractPrincipalCollection {
* @return INode
*/
public function getChildForPrincipal(array $principalInfo) {
- list(,$name) = \Sabre\Uri\split($principalInfo['uri']);
+ 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);
+ return new VersionHome($principalInfo, $this->rootFolder, $this->userManager, $this->versionManager);
}
public function getName() {
diff --git a/apps/files_versions/lib/Sabre/VersionCollection.php b/apps/files_versions/lib/Sabre/VersionCollection.php
index 481a5f491c3..9a3a6a365f0 100644
--- a/apps/files_versions/lib/Sabre/VersionCollection.php
+++ b/apps/files_versions/lib/Sabre/VersionCollection.php
@@ -21,11 +21,15 @@ declare(strict_types=1);
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+
namespace OCA\Files_Versions\Sabre;
use OCA\Files_Versions\Storage;
+use OCA\Files_Versions\Versions\IVersion;
+use OCA\Files_Versions\Versions\IVersionManager;
use OCP\Files\File;
use OCP\Files\Folder;
+use OCP\IUser;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
@@ -37,13 +41,17 @@ class VersionCollection implements ICollection {
/** @var File */
private $file;
- /** @var string */
- private $userId;
+ /** @var IUser */
+ private $user;
+
+ /** @var IVersionManager */
+ private $versionManager;
- public function __construct(Folder $userFolder, File $file, string $userId) {
+ public function __construct(Folder $userFolder, File $file, IUser $user, IVersionManager $versionManager) {
$this->userFolder = $userFolder;
$this->file = $file;
- $this->userId = $userId;
+ $this->user = $user;
+ $this->versionManager = $versionManager;
}
public function createFile($name, $data = null) {
@@ -68,10 +76,10 @@ class VersionCollection implements ICollection {
}
public function getChildren(): array {
- $versions = Storage::getVersions($this->userId, $this->userFolder->getRelativePath($this->file->getPath()));
+ $versions = $this->versionManager->getVersionsForFile($this->user, $this->file);
- return array_map(function (array $data) {
- return new VersionFile($data, $this->userFolder->getParent());
+ return array_map(function (IVersion $version) {
+ return new VersionFile($version, $this->versionManager);
}, $versions);
}
diff --git a/apps/files_versions/lib/Sabre/VersionFile.php b/apps/files_versions/lib/Sabre/VersionFile.php
index 347058448fc..2d630008d2a 100644
--- a/apps/files_versions/lib/Sabre/VersionFile.php
+++ b/apps/files_versions/lib/Sabre/VersionFile.php
@@ -21,26 +21,26 @@ declare(strict_types=1);
* 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 OCA\Files_Versions\Versions\IVersion;
+use OCA\Files_Versions\Versions\IVersionManager;
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 IVersion */
+ private $version;
- /** @var Folder */
- private $userRoot;
+ /** @var IVersionManager */
+ private $versionManager;
- public function __construct(array $data, Folder $userRoot) {
- $this->data = $data;
- $this->userRoot = $userRoot;
+ public function __construct(IVersion $version, IVersionManager $versionManager) {
+ $this->version = $version;
+ $this->versionManager = $versionManager;
}
public function put($data) {
@@ -49,27 +49,22 @@ class VersionFile implements IFile {
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']);
+ return $this->versionManager->read($this->version);
} catch (NotFoundException $e) {
throw new NotFound();
}
-
- return $version->fopen('rb');
}
public function getContentType(): string {
- return $this->data['mimetype'];
+ return $this->version->getMimeType();
}
public function getETag(): string {
- return $this->data['version'];
+ return (string)$this->version->getRevisionId();
}
public function getSize(): int {
- return $this->data['size'];
+ return $this->version->getSize();
}
public function delete() {
@@ -77,7 +72,7 @@ class VersionFile implements IFile {
}
public function getName(): string {
- return $this->data['version'];
+ return (string)$this->version->getRevisionId();
}
public function setName($name) {
@@ -85,10 +80,10 @@ class VersionFile implements IFile {
}
public function getLastModified(): int {
- return (int)$this->data['version'];
+ return $this->version->getTimestamp();
}
- public function rollBack(): bool {
- return Storage::rollback($this->data['path'], $this->data['version']);
+ public function rollBack() {
+ $this->versionManager->rollback($this->version);
}
}
diff --git a/apps/files_versions/lib/Sabre/VersionHome.php b/apps/files_versions/lib/Sabre/VersionHome.php
index 7a99d2376d4..7be5974bbbe 100644
--- a/apps/files_versions/lib/Sabre/VersionHome.php
+++ b/apps/files_versions/lib/Sabre/VersionHome.php
@@ -20,9 +20,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+
namespace OCA\Files_Versions\Sabre;
+use OC\User\NoUserException;
+use OCA\Files_Versions\Versions\IVersionManager;
use OCP\Files\IRootFolder;
+use OCP\IUserManager;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\ICollection;
@@ -34,9 +38,25 @@ class VersionHome implements ICollection {
/** @var IRootFolder */
private $rootFolder;
- public function __construct(array $principalInfo, IRootFolder $rootFolder) {
+ /** @var IUserManager */
+ private $userManager;
+
+ /** @var IVersionManager */
+ private $versionManager;
+
+ public function __construct(array $principalInfo, IRootFolder $rootFolder, IUserManager $userManager, IVersionManager $versionManager) {
$this->principalInfo = $principalInfo;
$this->rootFolder = $rootFolder;
+ $this->userManager = $userManager;
+ $this->versionManager = $versionManager;
+ }
+
+ private function getUser() {
+ list(, $name) = \Sabre\Uri\split($this->principalInfo['uri']);
+ $user = $this->userManager->get($name);
+ if (!$user) {
+ throw new NoUserException();
+ }
}
public function delete() {
@@ -44,8 +64,7 @@ class VersionHome implements ICollection {
}
public function getName(): string {
- list(,$name) = \Sabre\Uri\split($this->principalInfo['uri']);
- return $name;
+ return $this->getUser()->getUID();
}
public function setName($name) {
@@ -61,22 +80,22 @@ class VersionHome implements ICollection {
}
public function getChild($name) {
- list(,$userId) = \Sabre\Uri\split($this->principalInfo['uri']);
+ $user = $this->getUser();
if ($name === 'versions') {
- return new VersionRoot($userId, $this->rootFolder);
+ return new VersionRoot($user, $this->rootFolder, $this->versionManager);
}
if ($name === 'restore') {
- return new RestoreFolder($userId);
+ return new RestoreFolder();
}
}
public function getChildren() {
- list(,$userId) = \Sabre\Uri\split($this->principalInfo['uri']);
+ $user = $this->getUser();
return [
- new VersionRoot($userId, $this->rootFolder),
- new RestoreFolder($userId),
+ new VersionRoot($user, $this->rootFolder, $this->versionManager),
+ new RestoreFolder(),
];
}
diff --git a/apps/files_versions/lib/Sabre/VersionRoot.php b/apps/files_versions/lib/Sabre/VersionRoot.php
index 743b1c6ef1b..1c689a4d87b 100644
--- a/apps/files_versions/lib/Sabre/VersionRoot.php
+++ b/apps/files_versions/lib/Sabre/VersionRoot.php
@@ -23,23 +23,29 @@ declare(strict_types=1);
*/
namespace OCA\Files_Versions\Sabre;
+use OCA\Files_Versions\Versions\IVersionManager;
use OCP\Files\File;
use OCP\Files\IRootFolder;
+use OCP\IUser;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
class VersionRoot implements ICollection {
- /** @var string */
- private $userId;
+ /** @var IUser */
+ private $user;
/** @var IRootFolder */
private $rootFolder;
- public function __construct(string $userId, IRootFolder $rootFolder) {
- $this->userId = $userId;
+ /** @var IVersionManager */
+ private $versionManager;
+
+ public function __construct(IUser $user, IRootFolder $rootFolder, IVersionManager $versionManager) {
+ $this->user = $user;
$this->rootFolder = $rootFolder;
+ $this->versionManager = $versionManager;
}
public function delete() {
@@ -63,7 +69,7 @@ class VersionRoot implements ICollection {
}
public function getChild($name) {
- $userFolder = $this->rootFolder->getUserFolder($this->userId);
+ $userFolder = $this->rootFolder->getUserFolder($this->user->getUID());
$fileId = (int)$name;
$nodes = $userFolder->getById($fileId);
@@ -78,7 +84,7 @@ class VersionRoot implements ICollection {
throw new NotFound();
}
- return new VersionCollection($userFolder, $node, $this->userId);
+ return new VersionCollection($userFolder, $node, $this->user, $this->versionManager);
}
public function getChildren(): array {
diff --git a/apps/files_versions/lib/Storage.php b/apps/files_versions/lib/Storage.php
index 401544cc5d7..e2e4888cbce 100644
--- a/apps/files_versions/lib/Storage.php
+++ b/apps/files_versions/lib/Storage.php
@@ -48,6 +48,7 @@ use OC\Files\View;
use OCA\Files_Versions\AppInfo\Application;
use OCA\Files_Versions\Command\Expire;
use OCA\Files_Versions\Events\CreateVersionEvent;
+use OCA\Files_Versions\Versions\IVersionManager;
use OCP\Files\NotFoundException;
use OCP\Lock\ILockingProvider;
use OCP\User;
@@ -178,10 +179,10 @@ class Storage {
list($uid, $filename) = self::getUidAndFilename($filename);
$files_view = new View('/'.$uid .'/files');
- $users_view = new View('/'.$uid);
$eventDispatcher = \OC::$server->getEventDispatcher();
- $id = $files_view->getFileInfo($filename)->getId();
+ $fileInfo = $files_view->getFileInfo($filename);
+ $id = $fileInfo->getId();
$nodes = \OC::$server->getRootFolder()->getById($id);
foreach ($nodes as $node) {
$event = new CreateVersionEvent($node);
@@ -192,20 +193,16 @@ class Storage {
}
// no use making versions for empty files
- if ($files_view->filesize($filename) === 0) {
+ if ($fileInfo->getSize() === 0) {
return false;
}
- // create all parent folders
- self::createMissingDirectories($filename, $users_view);
-
- self::scheduleExpire($uid, $filename);
+ /** @var IVersionManager $versionManager */
+ $versionManager = \OC::$server->query(IVersionManager::class);
+ $userManager = \OC::$server->getUserManager();
+ $user = $userManager->get($uid);
- // store a new version of a file
- $mtime = $users_view->filemtime('files/' . $filename);
- $users_view->copy('files/' . $filename, 'files_versions/' . $filename . '.v' . $mtime);
- // call getFileInfo to enforce a file cache entry for the new version
- $users_view->getFileInfo('files_versions/' . $filename . '.v' . $mtime);
+ $versionManager->createVersion($user, $fileInfo);
}
@@ -695,7 +692,7 @@ class Storage {
* @param string $uid owner of the file
* @param string $fileName file/folder for which to schedule expiration
*/
- private static function scheduleExpire($uid, $fileName) {
+ public static function scheduleExpire($uid, $fileName) {
// let the admin disable auto expire
$expiration = self::getExpiration();
if ($expiration->isEnabled()) {
@@ -833,7 +830,7 @@ class Storage {
* "files" folder
* @param View $view view on data/user/
*/
- private static function createMissingDirectories($filename, $view) {
+ public static function createMissingDirectories($filename, $view) {
$dirname = Filesystem::normalizePath(dirname($filename));
$dirParts = explode('/', $dirname);
$dir = "/files_versions";
diff --git a/apps/files_versions/lib/Versions/BackendNotFoundException.php b/apps/files_versions/lib/Versions/BackendNotFoundException.php
new file mode 100644
index 00000000000..09985a716b9
--- /dev/null
+++ b/apps/files_versions/lib/Versions/BackendNotFoundException.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.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\Versions;
+
+class BackendNotFoundException extends \Exception {
+
+}
diff --git a/apps/files_versions/lib/Versions/IVersion.php b/apps/files_versions/lib/Versions/IVersion.php
new file mode 100644
index 00000000000..b6fc95814d8
--- /dev/null
+++ b/apps/files_versions/lib/Versions/IVersion.php
@@ -0,0 +1,99 @@
+<?php
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.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\Versions;
+
+use OCP\Files\FileInfo;
+use OCP\IUser;
+
+/**
+ * @since 15.0.0
+ */
+interface IVersion {
+ /**
+ * @return IVersionBackend
+ * @since 15.0.0
+ */
+ public function getBackend(): IVersionBackend;
+
+ /**
+ * Get the file info of the source file
+ *
+ * @return FileInfo
+ * @since 15.0.0
+ */
+ public function getSourceFile(): FileInfo;
+
+ /**
+ * Get the id of the revision for the file
+ *
+ * @return int
+ * @since 15.0.0
+ */
+ public function getRevisionId(): int;
+
+ /**
+ * Get the timestamp this version was created
+ *
+ * @return int
+ * @since 15.0.0
+ */
+ public function getTimestamp(): int;
+
+ /**
+ * Get the size of this version
+ *
+ * @return int
+ * @since 15.0.0
+ */
+ public function getSize(): int;
+
+ /**
+ * Get the name of the source file at the time of making this version
+ *
+ * @return string
+ * @since 15.0.0
+ */
+ public function getSourceFileName(): string;
+
+ /**
+ * Get the mimetype of this version
+ *
+ * @return string
+ * @since 15.0.0
+ */
+ public function getMimeType(): string;
+
+ /**
+ * Get the path of this version
+ *
+ * @return string
+ * @since 15.0.0
+ */
+ public function getVersionPath(): string;
+
+ /**
+ * @return IUser
+ * @since 15.0.0
+ */
+ public function getUser(): IUser;
+}
diff --git a/apps/files_versions/lib/Versions/IVersionBackend.php b/apps/files_versions/lib/Versions/IVersionBackend.php
new file mode 100644
index 00000000000..616d535f7fd
--- /dev/null
+++ b/apps/files_versions/lib/Versions/IVersionBackend.php
@@ -0,0 +1,81 @@
+<?php declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.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\Versions;
+
+use OCP\Files\File;
+use OCP\Files\FileInfo;
+use OCP\Files\NotFoundException;
+use OCP\Files\SimpleFS\ISimpleFile;
+use OCP\IUser;
+
+/**
+ * @since 15.0.0
+ */
+interface IVersionBackend {
+ /**
+ * Get all versions for a file
+ *
+ * @param IUser $user
+ * @param FileInfo $file
+ * @return IVersion[]
+ * @since 15.0.0
+ */
+ public function getVersionsForFile(IUser $user, FileInfo $file): array;
+
+ /**
+ * Create a new version for a file
+ *
+ * @param IUser $user
+ * @param FileInfo $file
+ * @since 15.0.0
+ */
+ public function createVersion(IUser $user, FileInfo $file);
+
+ /**
+ * Restore this version
+ *
+ * @param IVersion $version
+ * @since 15.0.0
+ */
+ public function rollback(IVersion $version);
+
+ /**
+ * Open the file for reading
+ *
+ * @param IVersion $version
+ * @return resource
+ * @throws NotFoundException
+ * @since 15.0.0
+ */
+ public function read(IVersion $version);
+
+ /**
+ * Get the preview for a specific version of a file
+ *
+ * @param IUser $user
+ * @param FileInfo $sourceFile
+ * @param int $revision
+ * @return ISimpleFile
+ * @since 15.0.0
+ */
+ public function getVersionFile(IUser $user, FileInfo $sourceFile, int $revision): File;
+}
diff --git a/apps/files_versions/lib/Versions/IVersionManager.php b/apps/files_versions/lib/Versions/IVersionManager.php
new file mode 100644
index 00000000000..748b649b1a2
--- /dev/null
+++ b/apps/files_versions/lib/Versions/IVersionManager.php
@@ -0,0 +1,36 @@
+<?php declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.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\Versions;
+
+/**
+ * @since 15.0.0
+ */
+interface IVersionManager extends IVersionBackend {
+ /**
+ * Register a new backend
+ *
+ * @param string $storageType
+ * @param IVersionBackend $backend
+ * @since 15.0.0
+ */
+ public function registerBackend(string $storageType, IVersionBackend $backend);
+}
diff --git a/apps/files_versions/lib/Versions/LegacyVersionsBackend.php b/apps/files_versions/lib/Versions/LegacyVersionsBackend.php
new file mode 100644
index 00000000000..7293aca641e
--- /dev/null
+++ b/apps/files_versions/lib/Versions/LegacyVersionsBackend.php
@@ -0,0 +1,105 @@
+<?php declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.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\Versions;
+
+use OC\Files\View;
+use OCA\Files_Versions\Storage;
+use OCP\Files\File;
+use OCP\Files\FileInfo;
+use OCP\Files\Folder;
+use OCP\Files\IRootFolder;
+use OCP\Files\NotFoundException;
+use OCP\IUser;
+
+class LegacyVersionsBackend implements IVersionBackend {
+ /** @var IRootFolder */
+ private $rootFolder;
+
+ public function __construct(IRootFolder $rootFolder) {
+ $this->rootFolder = $rootFolder;
+ }
+
+ public function getVersionsForFile(IUser $user, FileInfo $file): array {
+ $userFolder = $this->rootFolder->getUserFolder($user->getUID());
+ $versions = Storage::getVersions($user->getUID(), $userFolder->getRelativePath($file->getPath()));
+
+ return array_map(function (array $data) use ($file, $user) {
+ return new Version(
+ (int)$data['version'],
+ (int)$data['version'],
+ $data['name'],
+ (int)$data['size'],
+ $data['mimetype'],
+ $data['path'],
+ $file,
+ $this,
+ $user
+ );
+ }, $versions);
+ }
+
+ public function createVersion(IUser $user, FileInfo $file) {
+ $userFolder = $this->rootFolder->getUserFolder($user->getUID());
+ $relativePath = $userFolder->getRelativePath($file->getPath());
+ $userView = new View('/' . $user->getUID());
+ // create all parent folders
+ Storage::createMissingDirectories($relativePath, $userView);
+
+ Storage::scheduleExpire($user->getUID(), $relativePath);
+
+ // store a new version of a file
+ $userView->copy('files/' . $relativePath, 'files_versions/' . $relativePath . '.v' . $file->getMtime());
+ // ensure the file is scanned
+ $userView->getFileInfo('files_versions/' . $relativePath . '.v' . $file->getMtime());
+ }
+
+ public function rollback(IVersion $version) {
+ return Storage::rollback($version->getVersionPath(), $version->getRevisionId());
+ }
+
+ private function getVersionFolder(IUser $user): Folder {
+ $userRoot = $this->rootFolder->getUserFolder($user->getUID())
+ ->getParent();
+ try {
+ /** @var Folder $folder */
+ $folder = $userRoot->get('files_versions');
+ return $folder;
+ } catch (NotFoundException $e) {
+ return $userRoot->newFolder('files_versions');
+ }
+ }
+
+ public function read(IVersion $version) {
+ $versions = $this->getVersionFolder($version->getUser());
+ /** @var File $file */
+ $file = $versions->get($version->getVersionPath() . '.v' . $version->getRevisionId());
+ return $file->fopen('r');
+ }
+
+ public function getVersionFile(IUser $user, FileInfo $sourceFile, int $revision): File {
+ $userFolder = $this->rootFolder->getUserFolder($user->getUID());
+ $versionFolder = $this->getVersionFolder($user);
+ /** @var File $file */
+ $file = $versionFolder->get($userFolder->getRelativePath($sourceFile->getPath()) . '.v' . $revision);
+ return $file;
+ }
+}
diff --git a/apps/files_versions/lib/Versions/Version.php b/apps/files_versions/lib/Versions/Version.php
new file mode 100644
index 00000000000..5988234db61
--- /dev/null
+++ b/apps/files_versions/lib/Versions/Version.php
@@ -0,0 +1,113 @@
+<?php
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.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\Versions;
+
+use OCP\Files\FileInfo;
+use OCP\IUser;
+
+class Version implements IVersion {
+ /** @var int */
+ private $timestamp;
+
+ /** @var int */
+ private $revisionId;
+
+ /** @var string */
+ private $name;
+
+ /** @var int */
+ private $size;
+
+ /** @var string */
+ private $mimetype;
+
+ /** @var string */
+ private $path;
+
+ /** @var FileInfo */
+ private $sourceFileInfo;
+
+ /** @var IVersionBackend */
+ private $backend;
+
+ /** @var IUser */
+ private $user;
+
+ public function __construct(
+ int $timestamp,
+ int $revisionId,
+ string $name,
+ int $size,
+ string $mimetype,
+ string $path,
+ FileInfo $sourceFileInfo,
+ IVersionBackend $backend,
+ IUser $user
+ ) {
+ $this->timestamp = $timestamp;
+ $this->revisionId = $revisionId;
+ $this->name = $name;
+ $this->size = $size;
+ $this->mimetype = $mimetype;
+ $this->path = $path;
+ $this->sourceFileInfo = $sourceFileInfo;
+ $this->backend = $backend;
+ $this->user = $user;
+ }
+
+ public function getBackend(): IVersionBackend {
+ return $this->backend;
+ }
+
+ public function getSourceFile(): FileInfo {
+ return $this->sourceFileInfo;
+ }
+
+ public function getRevisionId(): int {
+ return $this->revisionId;
+ }
+
+ public function getTimestamp(): int {
+ return $this->timestamp;
+ }
+
+ public function getSize(): int {
+ return $this->size;
+ }
+
+ public function getSourceFileName(): string {
+ return $this->name;
+ }
+
+ public function getMimeType(): string {
+ return $this->mimetype;
+ }
+
+ public function getVersionPath(): string {
+ return $this->path;
+ }
+
+ public function getUser(): IUser {
+ return $this->user;
+ }
+}
diff --git a/apps/files_versions/lib/Versions/VersionManager.php b/apps/files_versions/lib/Versions/VersionManager.php
new file mode 100644
index 00000000000..757b6002710
--- /dev/null
+++ b/apps/files_versions/lib/Versions/VersionManager.php
@@ -0,0 +1,93 @@
+<?php declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.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\Versions;
+
+use OCP\Files\File;
+use OCP\Files\FileInfo;
+use OCP\Files\Storage\IStorage;
+use OCP\IUser;
+
+class VersionManager implements IVersionManager {
+ /** @var IVersionBackend[] */
+ private $backends = [];
+
+ public function registerBackend(string $storageType, IVersionBackend $backend) {
+ $this->backends[$storageType] = $backend;
+ }
+
+ /**
+ * @return IVersionBackend[]
+ */
+ private function getBackends(): array {
+ return $this->backends;
+ }
+
+ /**
+ * @param IStorage $storage
+ * @return IVersionBackend
+ * @throws BackendNotFoundException
+ */
+ public function getBackendForStorage(IStorage $storage): IVersionBackend {
+ $fullType = get_class($storage);
+ $backends = $this->getBackends();
+ $foundType = array_reduce(array_keys($backends), function ($type, $registeredType) use ($storage) {
+ if (
+ $storage->instanceOfStorage($registeredType) &&
+ ($type === '' || is_subclass_of($registeredType, $type))
+ ) {
+ return $registeredType;
+ } else {
+ return $type;
+ }
+ }, '');
+ if ($foundType === '') {
+ throw new BackendNotFoundException("Version backend for $fullType not found");
+ } else {
+ return $backends[$foundType];
+ }
+ }
+
+ public function getVersionsForFile(IUser $user, FileInfo $file): array {
+ $backend = $this->getBackendForStorage($file->getStorage());
+ return $backend->getVersionsForFile($user, $file);
+ }
+
+ public function createVersion(IUser $user, FileInfo $file) {
+ $backend = $this->getBackendForStorage($file->getStorage());
+ $backend->createVersion($user, $file);
+ }
+
+ public function rollback(IVersion $version) {
+ $backend = $version->getBackend();
+ return $backend->rollback($version);
+ }
+
+ public function read(IVersion $version) {
+ $backend = $version->getBackend();
+ return $backend->read($version);
+ }
+
+ public function getVersionFile(IUser $user, FileInfo $sourceFile, int $revision): File {
+ $backend = $this->getBackendForStorage($sourceFile->getStorage());
+ return $backend->getVersionFile($user, $sourceFile, $revision);
+ }
+}
diff --git a/apps/files_versions/tests/Controller/PreviewControllerTest.php b/apps/files_versions/tests/Controller/PreviewControllerTest.php
index 384f43cf495..7c248b36349 100644
--- a/apps/files_versions/tests/Controller/PreviewControllerTest.php
+++ b/apps/files_versions/tests/Controller/PreviewControllerTest.php
@@ -20,9 +20,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+
namespace OCA\Files_Versions\Tests\Controller;
+use OC\User\User;
use OCA\Files_Versions\Controller\PreviewController;
+use OCA\Files_Versions\Versions\IVersionManager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
@@ -34,6 +37,8 @@ use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IPreview;
use OCP\IRequest;
+use OCP\IUser;
+use OCP\IUserSession;
use Test\TestCase;
class PreviewControllerTest extends TestCase {
@@ -50,23 +55,39 @@ class PreviewControllerTest extends TestCase {
/** @var IPreview|\PHPUnit_Framework_MockObject_MockObject */
private $previewManager;
- /** @var PreviewController */
+ /** @var PreviewController|\PHPUnit_Framework_MockObject_MockObject */
private $controller;
+ /** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */
+ private $userSession;
+
+ /** @var IVersionManager|\PHPUnit_Framework_MockObject_MockObject */
+ private $versionManager;
+
public function setUp() {
parent::setUp();
$this->rootFolder = $this->createMock(IRootFolder::class);
$this->userId = 'user';
+ $user = $this->createMock(IUser::class);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn($this->userId);
$this->mimeTypeDetector = $this->createMock(IMimeTypeDetector::class);
$this->previewManager = $this->createMock(IPreview::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->versionManager = $this->createMock(IVersionManager::class);
$this->controller = new PreviewController(
'files_versions',
$this->createMock(IRequest::class),
$this->rootFolder,
- $this->userId,
+ $this->userSession,
$this->mimeTypeDetector,
+ $this->versionManager,
$this->previewManager
);
}
@@ -102,24 +123,23 @@ class PreviewControllerTest extends TestCase {
public function testValidPreview() {
$userFolder = $this->createMock(Folder::class);
$userRoot = $this->createMock(Folder::class);
- $versions = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->userId)
->willReturn($userFolder);
$userFolder->method('getParent')
->willReturn($userRoot);
- $userRoot->method('get')
- ->with('files_versions')
- ->willReturn($versions);
- $this->mimeTypeDetector->method('detectPath')
- ->with($this->equalTo('file'))
- ->willReturn('myMime');
+ $sourceFile = $this->createMock(File::class);
+ $userFolder->method('get')
+ ->with('file')
+ ->willReturn($sourceFile);
$file = $this->createMock(File::class);
- $versions->method('get')
- ->with($this->equalTo('file.v42'))
+ $file->method('getMimetype')
+ ->willReturn('myMime');
+
+ $this->versionManager->method('getVersionFile')
->willReturn($file);
$preview = $this->createMock(ISimpleFile::class);
@@ -138,24 +158,23 @@ class PreviewControllerTest extends TestCase {
public function testVersionNotFound() {
$userFolder = $this->createMock(Folder::class);
$userRoot = $this->createMock(Folder::class);
- $versions = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with($this->userId)
->willReturn($userFolder);
$userFolder->method('getParent')
->willReturn($userRoot);
- $userRoot->method('get')
- ->with('files_versions')
- ->willReturn($versions);
+
+ $sourceFile = $this->createMock(File::class);
+ $userFolder->method('get')
+ ->with('file')
+ ->willReturn($sourceFile);
$this->mimeTypeDetector->method('detectPath')
->with($this->equalTo('file'))
->willReturn('myMime');
- $file = $this->createMock(File::class);
- $versions->method('get')
- ->with($this->equalTo('file.v42'))
+ $this->versionManager->method('getVersionFile')
->willThrowException(new NotFoundException());
$res = $this->controller->getPreview('file', 10, 10, '42');
diff --git a/core/css/global.scss b/core/css/global.scss
index 9511d4324fa..06dc3688a2b 100644
--- a/core/css/global.scss
+++ b/core/css/global.scss
@@ -25,12 +25,12 @@
}
.hidden {
- display: none;
+ display: none !important; /* Hiding should take precedence */
}
.hidden-visually {
position: absolute;
- left:-10000px;
+ left: -10000px;
top: auto;
width: 1px;
height: 1px;
@@ -47,4 +47,4 @@
.inlineblock {
display: inline-block;
-} \ No newline at end of file
+}
diff --git a/core/css/header.scss b/core/css/header.scss
index e790f719a1f..879734097ae 100644
--- a/core/css/header.scss
+++ b/core/css/header.scss
@@ -561,6 +561,7 @@ nav[role='navigation'] {
span {
opacity: .6;
bottom: 2px;
+ z-index: -1; /* fix clickability issue - otherwise we need to move the span into the link */
}
/* Prominent app title for current and hovered/focused app */
diff --git a/core/l10n/cs.js b/core/l10n/cs.js
index a846eb1517a..37ab7ea022f 100644
--- a/core/l10n/cs.js
+++ b/core/l10n/cs.js
@@ -352,6 +352,7 @@ OC.L10N.register(
"For help, see the <a target=\"_blank\" rel=\"noreferrer noopener\" href=\"%s\">documentation</a>." : "Pro pomoc, nahlédněte do <a target=\"_blank\" rel=\"noreferrer noopener\" href=\"%s\">dokumentace</a>.",
"I know that if I continue doing the update via web UI has the risk, that the request runs into a timeout and could cause data loss, but I have a backup and know how to restore my instance in case of a failure." : "Beru na vědomí, že při aktualizaci skrze webové rozhraní hrozí nebezpečí vypršení požadavku, který může vyústit ve ztrátu dat. Mám pro takový případ zálohu a vím, jak ji v případě selhání obnovit.",
"Upgrade via web on my own risk" : "Na vlastní nebezpečí aktualizovat skrze web",
+ "Maintenance mode" : "Režim údržby",
"This %s instance is currently in maintenance mode, which may take a while." : "Tato instalace %s je právě ve stavu údržby a ta může chvíli trvat.",
"Contact your system administrator if this message persists or appeared unexpectedly." : "Pokud se tato zpráva objevuje opakovaně nebo nečekaně, obraťte se správce systému.",
"Updated \"%s\" to %s" : "Aktualizováno z „%s“ na %s",
diff --git a/core/l10n/cs.json b/core/l10n/cs.json
index 7d4184b5de1..edcc2917e54 100644
--- a/core/l10n/cs.json
+++ b/core/l10n/cs.json
@@ -350,6 +350,7 @@
"For help, see the <a target=\"_blank\" rel=\"noreferrer noopener\" href=\"%s\">documentation</a>." : "Pro pomoc, nahlédněte do <a target=\"_blank\" rel=\"noreferrer noopener\" href=\"%s\">dokumentace</a>.",
"I know that if I continue doing the update via web UI has the risk, that the request runs into a timeout and could cause data loss, but I have a backup and know how to restore my instance in case of a failure." : "Beru na vědomí, že při aktualizaci skrze webové rozhraní hrozí nebezpečí vypršení požadavku, který může vyústit ve ztrátu dat. Mám pro takový případ zálohu a vím, jak ji v případě selhání obnovit.",
"Upgrade via web on my own risk" : "Na vlastní nebezpečí aktualizovat skrze web",
+ "Maintenance mode" : "Režim údržby",
"This %s instance is currently in maintenance mode, which may take a while." : "Tato instalace %s je právě ve stavu údržby a ta může chvíli trvat.",
"Contact your system administrator if this message persists or appeared unexpectedly." : "Pokud se tato zpráva objevuje opakovaně nebo nečekaně, obraťte se správce systému.",
"Updated \"%s\" to %s" : "Aktualizováno z „%s“ na %s",
diff --git a/core/l10n/de.js b/core/l10n/de.js
index 2a14d6115a9..1be2a21a5c6 100644
--- a/core/l10n/de.js
+++ b/core/l10n/de.js
@@ -167,6 +167,7 @@ OC.L10N.register(
"Share to {name}" : "Mit {name} teilen",
"Copy link" : "Link kopieren",
"Link" : "Link",
+ "Hide download" : "Download verbergen",
"Password protect" : "Passwortschutz",
"Allow editing" : "Bearbeitung erlauben",
"Email link to person" : "Link per E-Mail verschicken",
@@ -212,6 +213,8 @@ OC.L10N.register(
"An error occurred. Please try again" : "Es ist ein Fehler aufgetreten. Bitte versuche es noch einmal",
"{sharee} (remote group)" : "{sharee} (externe Gruppe)",
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
+ "Home" : "Start",
+ "Other" : "Andere",
"Share" : "Teilen",
"Name or email address..." : "Name oder E-Mail-Adresse…",
"Name or federated cloud ID..." : "Name oder Federated-Cloud-ID…",
diff --git a/core/l10n/de.json b/core/l10n/de.json
index 569176bee77..f2063db8b24 100644
--- a/core/l10n/de.json
+++ b/core/l10n/de.json
@@ -165,6 +165,7 @@
"Share to {name}" : "Mit {name} teilen",
"Copy link" : "Link kopieren",
"Link" : "Link",
+ "Hide download" : "Download verbergen",
"Password protect" : "Passwortschutz",
"Allow editing" : "Bearbeitung erlauben",
"Email link to person" : "Link per E-Mail verschicken",
@@ -210,6 +211,8 @@
"An error occurred. Please try again" : "Es ist ein Fehler aufgetreten. Bitte versuche es noch einmal",
"{sharee} (remote group)" : "{sharee} (externe Gruppe)",
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
+ "Home" : "Start",
+ "Other" : "Andere",
"Share" : "Teilen",
"Name or email address..." : "Name oder E-Mail-Adresse…",
"Name or federated cloud ID..." : "Name oder Federated-Cloud-ID…",
diff --git a/core/l10n/de_DE.js b/core/l10n/de_DE.js
index 99ad786719f..bdcea5e0bb5 100644
--- a/core/l10n/de_DE.js
+++ b/core/l10n/de_DE.js
@@ -167,6 +167,7 @@ OC.L10N.register(
"Share to {name}" : "Mit {name} teilen",
"Copy link" : "Link kopieren",
"Link" : "Link",
+ "Hide download" : "Download verbergen",
"Password protect" : "Passwortschutz",
"Allow editing" : "Bearbeitung erlauben",
"Email link to person" : "Link per E-Mail verschicken",
@@ -212,6 +213,8 @@ OC.L10N.register(
"An error occurred. Please try again" : "Es ist ein Fehler aufgetreten. Bitte versuchen Sie es noch einmal",
"{sharee} (remote group)" : "{sharee} (Externe Gruppe)",
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
+ "Home" : "Start",
+ "Other" : "Andere",
"Share" : "Teilen",
"Name or email address..." : "Name oder E-Mail-Adresse…",
"Name or federated cloud ID..." : "Name oder Federated-Cloud-ID…",
diff --git a/core/l10n/de_DE.json b/core/l10n/de_DE.json
index 06dd9d5285e..7f844020d63 100644
--- a/core/l10n/de_DE.json
+++ b/core/l10n/de_DE.json
@@ -165,6 +165,7 @@
"Share to {name}" : "Mit {name} teilen",
"Copy link" : "Link kopieren",
"Link" : "Link",
+ "Hide download" : "Download verbergen",
"Password protect" : "Passwortschutz",
"Allow editing" : "Bearbeitung erlauben",
"Email link to person" : "Link per E-Mail verschicken",
@@ -210,6 +211,8 @@
"An error occurred. Please try again" : "Es ist ein Fehler aufgetreten. Bitte versuchen Sie es noch einmal",
"{sharee} (remote group)" : "{sharee} (Externe Gruppe)",
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
+ "Home" : "Start",
+ "Other" : "Andere",
"Share" : "Teilen",
"Name or email address..." : "Name oder E-Mail-Adresse…",
"Name or federated cloud ID..." : "Name oder Federated-Cloud-ID…",
diff --git a/core/l10n/it.js b/core/l10n/it.js
index 21bfccaf8db..0c502a16885 100644
--- a/core/l10n/it.js
+++ b/core/l10n/it.js
@@ -167,6 +167,7 @@ OC.L10N.register(
"Share to {name}" : "Condividi con {name}",
"Copy link" : "Copia collegamento",
"Link" : "Collegamento",
+ "Hide download" : "Nascondi scaricamento",
"Password protect" : "Proteggi con password",
"Allow editing" : "Consenti la modifica",
"Email link to person" : "Invia collegamento via email",
@@ -212,6 +213,8 @@ OC.L10N.register(
"An error occurred. Please try again" : "Si è verificato un errore. Prova ancora",
"{sharee} (remote group)" : "{sharee} (remote group)",
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
+ "Home" : "Home",
+ "Other" : "Altro",
"Share" : "Condividi",
"Name or email address..." : "Nome o indirizzo email...",
"Name or federated cloud ID..." : "Nome o ID di cloud federata...",
diff --git a/core/l10n/it.json b/core/l10n/it.json
index 0c4c03a3345..e93e3137276 100644
--- a/core/l10n/it.json
+++ b/core/l10n/it.json
@@ -165,6 +165,7 @@
"Share to {name}" : "Condividi con {name}",
"Copy link" : "Copia collegamento",
"Link" : "Collegamento",
+ "Hide download" : "Nascondi scaricamento",
"Password protect" : "Proteggi con password",
"Allow editing" : "Consenti la modifica",
"Email link to person" : "Invia collegamento via email",
@@ -210,6 +211,8 @@
"An error occurred. Please try again" : "Si è verificato un errore. Prova ancora",
"{sharee} (remote group)" : "{sharee} (remote group)",
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
+ "Home" : "Home",
+ "Other" : "Altro",
"Share" : "Condividi",
"Name or email address..." : "Nome o indirizzo email...",
"Name or federated cloud ID..." : "Nome o ID di cloud federata...",
diff --git a/core/l10n/pt_BR.js b/core/l10n/pt_BR.js
index 93294b787c8..db7a373647c 100644
--- a/core/l10n/pt_BR.js
+++ b/core/l10n/pt_BR.js
@@ -167,6 +167,7 @@ OC.L10N.register(
"Share to {name}" : "Compartilhar com {name}",
"Copy link" : "Copiar link",
"Link" : "Link",
+ "Hide download" : "Ocultar download",
"Password protect" : "Proteger com senha",
"Allow editing" : "Permitir edição",
"Email link to person" : "Enviar link por e-mail",
@@ -212,6 +213,8 @@ OC.L10N.register(
"An error occurred. Please try again" : "Ocorreu um erro. Tente novamente",
"{sharee} (remote group)" : "{sharee} (grupo remoto)",
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
+ "Home" : "Home",
+ "Other" : "Outro",
"Share" : "Compartilhar",
"Name or email address..." : "Nome ou endereço de e-mail...",
"Name or federated cloud ID..." : "Nome ou ID de cloud federada...",
diff --git a/core/l10n/pt_BR.json b/core/l10n/pt_BR.json
index 91467fe3081..99330296c3b 100644
--- a/core/l10n/pt_BR.json
+++ b/core/l10n/pt_BR.json
@@ -165,6 +165,7 @@
"Share to {name}" : "Compartilhar com {name}",
"Copy link" : "Copiar link",
"Link" : "Link",
+ "Hide download" : "Ocultar download",
"Password protect" : "Proteger com senha",
"Allow editing" : "Permitir edição",
"Email link to person" : "Enviar link por e-mail",
@@ -210,6 +211,8 @@
"An error occurred. Please try again" : "Ocorreu um erro. Tente novamente",
"{sharee} (remote group)" : "{sharee} (grupo remoto)",
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
+ "Home" : "Home",
+ "Other" : "Outro",
"Share" : "Compartilhar",
"Name or email address..." : "Nome ou endereço de e-mail...",
"Name or federated cloud ID..." : "Nome ou ID de cloud federada...",
diff --git a/core/l10n/tr.js b/core/l10n/tr.js
index 4c4642705a2..080a6bc46f8 100644
--- a/core/l10n/tr.js
+++ b/core/l10n/tr.js
@@ -167,6 +167,7 @@ OC.L10N.register(
"Share to {name}" : "{name} ile paylaş",
"Copy link" : "Bağlantıyı kopyala",
"Link" : "Bağlantı",
+ "Hide download" : "İndirmeyi gizle",
"Password protect" : "Parola koruması",
"Allow editing" : "Düzenlenebilsin",
"Email link to person" : "Bağlantıyı e-posta ile gönder",
@@ -212,6 +213,8 @@ OC.L10N.register(
"An error occurred. Please try again" : "Bir sorun çıktı. Lütfen yeniden deneyin",
"{sharee} (remote group)" : "{sharee} (uzak grup)",
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
+ "Home" : "Giriş",
+ "Other" : "Diğer",
"Share" : "Paylaş",
"Name or email address..." : "Ad ya da e-posta adresi...",
"Name or federated cloud ID..." : "Ad ya da birleşmiş bulut kodu...",
diff --git a/core/l10n/tr.json b/core/l10n/tr.json
index 786e961725e..fb66979b76f 100644
--- a/core/l10n/tr.json
+++ b/core/l10n/tr.json
@@ -165,6 +165,7 @@
"Share to {name}" : "{name} ile paylaş",
"Copy link" : "Bağlantıyı kopyala",
"Link" : "Bağlantı",
+ "Hide download" : "İndirmeyi gizle",
"Password protect" : "Parola koruması",
"Allow editing" : "Düzenlenebilsin",
"Email link to person" : "Bağlantıyı e-posta ile gönder",
@@ -210,6 +211,8 @@
"An error occurred. Please try again" : "Bir sorun çıktı. Lütfen yeniden deneyin",
"{sharee} (remote group)" : "{sharee} (uzak grup)",
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
+ "Home" : "Giriş",
+ "Other" : "Diğer",
"Share" : "Paylaş",
"Name or email address..." : "Ad ya da e-posta adresi...",
"Name or federated cloud ID..." : "Ad ya da birleşmiş bulut kodu...",
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index c94837ed15c..8c6dc502487 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -231,6 +231,7 @@ return array(
'OCP\\Files\\Storage\\INotifyStorage' => $baseDir . '/lib/public/Files/Storage/INotifyStorage.php',
'OCP\\Files\\Storage\\IStorage' => $baseDir . '/lib/public/Files/Storage/IStorage.php',
'OCP\\Files\\Storage\\IStorageFactory' => $baseDir . '/lib/public/Files/Storage/IStorageFactory.php',
+ 'OCP\\Files\\Storage\\IWriteStreamStorage' => $baseDir . '/lib/public/Files/Storage/IWriteStreamStorage.php',
'OCP\\Files\\UnseekableException' => $baseDir . '/lib/public/Files/UnseekableException.php',
'OCP\\Files_FullTextSearch\\Model\\AFilesDocument' => $baseDir . '/lib/public/Files_FullTextSearch/Model/AFilesDocument.php',
'OCP\\FullTextSearch\\Exceptions\\FullTextSearchAppNotAvailableException' => $baseDir . '/lib/public/FullTextSearch/Exceptions/FullTextSearchAppNotAvailableException.php',
@@ -821,6 +822,7 @@ return array(
'OC\\Files\\Storage\\Wrapper\\PermissionsMask' => $baseDir . '/lib/private/Files/Storage/Wrapper/PermissionsMask.php',
'OC\\Files\\Storage\\Wrapper\\Quota' => $baseDir . '/lib/private/Files/Storage/Wrapper/Quota.php',
'OC\\Files\\Storage\\Wrapper\\Wrapper' => $baseDir . '/lib/private/Files/Storage/Wrapper/Wrapper.php',
+ 'OC\\Files\\Stream\\CountReadStream' => $baseDir . '/lib/private/Files/Stream/CountReadStream.php',
'OC\\Files\\Stream\\Encryption' => $baseDir . '/lib/private/Files/Stream/Encryption.php',
'OC\\Files\\Stream\\Quota' => $baseDir . '/lib/private/Files/Stream/Quota.php',
'OC\\Files\\Type\\Detection' => $baseDir . '/lib/private/Files/Type/Detection.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 80aabae2389..2a46e99e020 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -261,6 +261,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\Files\\Storage\\INotifyStorage' => __DIR__ . '/../../..' . '/lib/public/Files/Storage/INotifyStorage.php',
'OCP\\Files\\Storage\\IStorage' => __DIR__ . '/../../..' . '/lib/public/Files/Storage/IStorage.php',
'OCP\\Files\\Storage\\IStorageFactory' => __DIR__ . '/../../..' . '/lib/public/Files/Storage/IStorageFactory.php',
+ 'OCP\\Files\\Storage\\IWriteStreamStorage' => __DIR__ . '/../../..' . '/lib/public/Files/Storage/IWriteStreamStorage.php',
'OCP\\Files\\UnseekableException' => __DIR__ . '/../../..' . '/lib/public/Files/UnseekableException.php',
'OCP\\Files_FullTextSearch\\Model\\AFilesDocument' => __DIR__ . '/../../..' . '/lib/public/Files_FullTextSearch/Model/AFilesDocument.php',
'OCP\\FullTextSearch\\Exceptions\\FullTextSearchAppNotAvailableException' => __DIR__ . '/../../..' . '/lib/public/FullTextSearch/Exceptions/FullTextSearchAppNotAvailableException.php',
@@ -851,6 +852,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Files\\Storage\\Wrapper\\PermissionsMask' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/PermissionsMask.php',
'OC\\Files\\Storage\\Wrapper\\Quota' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Quota.php',
'OC\\Files\\Storage\\Wrapper\\Wrapper' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Wrapper.php',
+ 'OC\\Files\\Stream\\CountReadStream' => __DIR__ . '/../../..' . '/lib/private/Files/Stream/CountReadStream.php',
'OC\\Files\\Stream\\Encryption' => __DIR__ . '/../../..' . '/lib/private/Files/Stream/Encryption.php',
'OC\\Files\\Stream\\Quota' => __DIR__ . '/../../..' . '/lib/private/Files/Stream/Quota.php',
'OC\\Files\\Type\\Detection' => __DIR__ . '/../../..' . '/lib/private/Files/Type/Detection.php',
diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php
index 3ce919a4cbe..71acd27783c 100644
--- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php
+++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php
@@ -28,6 +28,7 @@ namespace OC\Files\ObjectStore;
use Icewind\Streams\CallbackWrapper;
use Icewind\Streams\IteratorDirectory;
use OC\Files\Cache\CacheEntry;
+use OC\Files\Stream\CountReadStream;
use OCP\Files\ObjectStore\IObjectStore;
class ObjectStoreStorage extends \OC\Files\Storage\Common {
@@ -382,25 +383,48 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
}
public function writeBack($tmpFile, $path) {
+ $size = filesize($tmpFile);
+ $this->writeStream($path, fopen($tmpFile, 'r'), $size);
+ }
+
+ /**
+ * external changes are not supported, exclusive access to the object storage is assumed
+ *
+ * @param string $path
+ * @param int $time
+ * @return false
+ */
+ public function hasUpdated($path, $time) {
+ return false;
+ }
+
+ public function needsPartFile() {
+ return false;
+ }
+
+ public function file_put_contents($path, $data) {
+ $stream = fopen('php://temp', 'r+');
+ fwrite($stream, $data);
+ rewind($stream);
+ return $this->writeStream($path, $stream, strlen($data)) > 0;
+ }
+
+ public function writeStream(string $path, $stream, int $size = null): int {
$stat = $this->stat($path);
if (empty($stat)) {
// create new file
- $stat = array(
+ $stat = [
'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
- );
+ ];
}
// update stat with new data
$mTime = time();
- $stat['size'] = filesize($tmpFile);
+ $stat['size'] = (int)$size;
$stat['mtime'] = $mTime;
$stat['storage_mtime'] = $mTime;
- // run path based detection first, to use file extension because $tmpFile is only a random string
$mimetypeDetector = \OC::$server->getMimeTypeDetector();
$mimetype = $mimetypeDetector->detectPath($path);
- if ($mimetype === 'application/octet-stream') {
- $mimetype = $mimetypeDetector->detect($tmpFile);
- }
$stat['mimetype'] = $mimetype;
$stat['etag'] = $this->getETag($path);
@@ -408,7 +432,20 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
$fileId = $this->getCache()->put($path, $stat);
try {
//upload to object storage
- $this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r'));
+ if ($size === null) {
+ $countStream = CountReadStream::wrap($stream, function ($writtenSize) use ($fileId, &$size) {
+ $this->getCache()->update($fileId, [
+ 'size' => $writtenSize
+ ]);
+ $size = $writtenSize;
+ });
+ $this->objectStore->writeObject($this->getURN($fileId), $countStream);
+ if (is_resource($countStream)) {
+ fclose($countStream);
+ }
+ } else {
+ $this->objectStore->writeObject($this->getURN($fileId), $stream);
+ }
} catch (\Exception $ex) {
$this->getCache()->remove($path);
$this->logger->logException($ex, [
@@ -417,20 +454,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
]);
throw $ex; // make this bubble up
}
- }
- /**
- * external changes are not supported, exclusive access to the object storage is assumed
- *
- * @param string $path
- * @param int $time
- * @return false
- */
- public function hasUpdated($path, $time) {
- return false;
- }
-
- public function needsPartFile() {
- return false;
+ return $size;
}
}
diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php
index b6c82f3a1df..72fe3a79792 100644
--- a/lib/private/Files/Storage/Common.php
+++ b/lib/private/Files/Storage/Common.php
@@ -54,6 +54,7 @@ use OCP\Files\InvalidPathException;
use OCP\Files\ReservedWordException;
use OCP\Files\Storage\ILockingStorage;
use OCP\Files\Storage\IStorage;
+use OCP\Files\Storage\IWriteStreamStorage;
use OCP\ILogger;
use OCP\Lock\ILockingProvider;
use OCP\Lock\LockedException;
@@ -69,7 +70,7 @@ use OCP\Lock\LockedException;
* Some \OC\Files\Storage\Common methods call functions which are first defined
* in classes which extend it, e.g. $this->stat() .
*/
-abstract class Common implements Storage, ILockingStorage {
+abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage {
use LocalTempFileTrait;
@@ -809,4 +810,23 @@ abstract class Common implements Storage, ILockingStorage {
public function needsPartFile() {
return true;
}
+
+ /**
+ * fallback implementation
+ *
+ * @param string $path
+ * @param resource $stream
+ * @param int $size
+ * @return int
+ */
+ public function writeStream(string $path, $stream, int $size = null): int {
+ $target = $this->fopen($path, 'w');
+ if (!$target) {
+ return 0;
+ }
+ list($count, $result) = \OC_Helper::streamCopy($stream, $target);
+ fclose($stream);
+ fclose($target);
+ return $count;
+ }
}
diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php
index 46b53dcf95c..5f7232e64b3 100644
--- a/lib/private/Files/Storage/Local.php
+++ b/lib/private/Files/Storage/Local.php
@@ -462,4 +462,8 @@ class Local extends \OC\Files\Storage\Common {
return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
}
}
+
+ public function writeStream(string $path, $stream, int $size = null): int {
+ return (int)file_put_contents($this->getSourcePath($path), $stream);
+ }
}
diff --git a/lib/private/Files/Storage/Wrapper/Encryption.php b/lib/private/Files/Storage/Wrapper/Encryption.php
index 42653b2d4a6..e1c1225e0cc 100644
--- a/lib/private/Files/Storage/Wrapper/Encryption.php
+++ b/lib/private/Files/Storage/Wrapper/Encryption.php
@@ -1029,4 +1029,13 @@ class Encryption extends Wrapper {
}
+ public function writeStream(string $path, $stream, int $size = null): int {
+ // always fall back to fopen
+ $target = $this->fopen($path, 'w');
+ list($count, $result) = \OC_Helper::streamCopy($stream, $target);
+ fclose($stream);
+ fclose($target);
+ return $count;
+ }
+
}
diff --git a/lib/private/Files/Storage/Wrapper/Jail.php b/lib/private/Files/Storage/Wrapper/Jail.php
index 56514af6d80..f21b5716467 100644
--- a/lib/private/Files/Storage/Wrapper/Jail.php
+++ b/lib/private/Files/Storage/Wrapper/Jail.php
@@ -29,6 +29,7 @@ use OC\Files\Cache\Wrapper\CacheJail;
use OC\Files\Cache\Wrapper\JailPropagator;
use OC\Files\Filesystem;
use OCP\Files\Storage\IStorage;
+use OCP\Files\Storage\IWriteStreamStorage;
use OCP\Lock\ILockingProvider;
/**
@@ -515,4 +516,18 @@ class Jail extends Wrapper {
$this->propagator = new JailPropagator($storage, \OC::$server->getDatabaseConnection());
return $this->propagator;
}
+
+ public function writeStream(string $path, $stream, int $size = null): int {
+ $storage = $this->getWrapperStorage();
+ if ($storage->instanceOfStorage(IWriteStreamStorage::class)) {
+ /** @var IWriteStreamStorage $storage */
+ return $storage->writeStream($this->getUnjailedPath($path), $stream, $size);
+ } else {
+ $target = $this->fopen($path, 'w');
+ list($count, $result) = \OC_Helper::streamCopy($stream, $target);
+ fclose($stream);
+ fclose($target);
+ return $count;
+ }
+ }
}
diff --git a/lib/private/Files/Storage/Wrapper/Wrapper.php b/lib/private/Files/Storage/Wrapper/Wrapper.php
index 060c596ad65..f9c84b89fe5 100644
--- a/lib/private/Files/Storage/Wrapper/Wrapper.php
+++ b/lib/private/Files/Storage/Wrapper/Wrapper.php
@@ -32,9 +32,10 @@ namespace OC\Files\Storage\Wrapper;
use OCP\Files\InvalidPathException;
use OCP\Files\Storage\ILockingStorage;
use OCP\Files\Storage\IStorage;
+use OCP\Files\Storage\IWriteStreamStorage;
use OCP\Lock\ILockingProvider;
-class Wrapper implements \OC\Files\Storage\Storage, ILockingStorage {
+class Wrapper implements \OC\Files\Storage\Storage, ILockingStorage, IWriteStreamStorage {
/**
* @var \OC\Files\Storage\Storage $storage
*/
@@ -621,4 +622,18 @@ class Wrapper implements \OC\Files\Storage\Storage, ILockingStorage {
public function needsPartFile() {
return $this->getWrapperStorage()->needsPartFile();
}
+
+ public function writeStream(string $path, $stream, int $size = null): int {
+ $storage = $this->getWrapperStorage();
+ if ($storage->instanceOfStorage(IWriteStreamStorage::class)) {
+ /** @var IWriteStreamStorage $storage */
+ return $storage->writeStream($path, $stream, $size);
+ } else {
+ $target = $this->fopen($path, 'w');
+ list($count, $result) = \OC_Helper::streamCopy($stream, $target);
+ fclose($stream);
+ fclose($target);
+ return $count;
+ }
+ }
}
diff --git a/lib/private/Files/Stream/CountReadStream.php b/lib/private/Files/Stream/CountReadStream.php
new file mode 100644
index 00000000000..93cadf8f214
--- /dev/null
+++ b/lib/private/Files/Stream/CountReadStream.php
@@ -0,0 +1,65 @@
+<?php declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.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 OC\Files\Stream;
+
+use Icewind\Streams\Wrapper;
+
+class CountReadStream extends Wrapper {
+ /** @var int */
+ private $count;
+
+ /** @var callback */
+ private $callback;
+
+ public static function wrap($source, $callback) {
+ $context = stream_context_create(array(
+ 'count' => array(
+ 'source' => $source,
+ 'callback' => $callback,
+ )
+ ));
+ return Wrapper::wrapSource($source, $context, 'count', self::class);
+ }
+
+ public function dir_opendir($path, $options) {
+ return false;
+ }
+
+ public function stream_open($path, $mode, $options, &$opened_path) {
+ $context = $this->loadContext('count');
+
+ $this->callback = $context['callback'];
+ return true;
+ }
+
+ public function stream_read($count) {
+ $result = parent::stream_read($count);
+ $this->count += strlen($result);
+ return $result;
+ }
+
+ public function stream_close() {
+ $result = parent::stream_close();
+ call_user_func($this->callback, $this->count);
+ return $result;
+ }
+}
diff --git a/lib/private/FullTextSearch/FullTextSearchManager.php b/lib/private/FullTextSearch/FullTextSearchManager.php
index 6529ef2506a..9a9b077cf23 100644
--- a/lib/private/FullTextSearch/FullTextSearchManager.php
+++ b/lib/private/FullTextSearch/FullTextSearchManager.php
@@ -195,7 +195,7 @@ class FullTextSearchManager implements IFullTextSearchManager {
* @throws FullTextSearchAppNotAvailableException
*/
public function updateIndexesStatus(string $providerId, array $documentIds, int $status, bool $reset = false) {
- $this->getIndexService()->updateIndexStatus($providerId, $documentIds, $status, $reset);
+ $this->getIndexService()->updateIndexesStatus($providerId, $documentIds, $status, $reset);
}
diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php
index 86579e3480b..1f7decf2b79 100644
--- a/lib/private/Preview/Generator.php
+++ b/lib/private/Preview/Generator.php
@@ -298,19 +298,23 @@ class Generator {
if ($height !== $maxHeight && $width !== $maxWidth) {
/*
- * Scale to the nearest power of two
+ * Scale to the nearest power of four
*/
- $pow2height = 2 ** ceil(log($height) / log(2));
- $pow2width = 2 ** ceil(log($width) / log(2));
+ $pow4height = 4 ** ceil(log($height) / log(4));
+ $pow4width = 4 ** ceil(log($width) / log(4));
- $ratioH = $height / $pow2height;
- $ratioW = $width / $pow2width;
+ // Minimum size is 64
+ $pow4height = max($pow4height, 64);
+ $pow4width = max($pow4width, 64);
+
+ $ratioH = $height / $pow4height;
+ $ratioW = $width / $pow4width;
if ($ratioH < $ratioW) {
- $width = $pow2width;
+ $width = $pow4width;
$height /= $ratioW;
} else {
- $height = $pow2height;
+ $height = $pow4height;
$width /= $ratioH;
}
}
diff --git a/lib/public/Files/Storage/IWriteStreamStorage.php b/lib/public/Files/Storage/IWriteStreamStorage.php
new file mode 100644
index 00000000000..39a28dd037b
--- /dev/null
+++ b/lib/public/Files/Storage/IWriteStreamStorage.php
@@ -0,0 +1,40 @@
+<?php declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.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 OCP\Files\Storage;
+
+/**
+ * Interface that adds the ability to write a stream directly to file
+ *
+ * @since 15.0.0
+ */
+interface IWriteStreamStorage extends IStorage {
+ /**
+ * Write the data from a stream to a file
+ *
+ * @param string $path
+ * @param resource $stream
+ * @param int|null $size the size of the stream if known in advance
+ * @return int the number of bytes written
+ * @since 15.0.0
+ */
+ public function writeStream(string $path, $stream, int $size = null): int;
+}
diff --git a/lib/public/FullTextSearch/Service/IIndexService.php b/lib/public/FullTextSearch/Service/IIndexService.php
index 376c07a4490..c5d1b9b3bcf 100644
--- a/lib/public/FullTextSearch/Service/IIndexService.php
+++ b/lib/public/FullTextSearch/Service/IIndexService.php
@@ -65,11 +65,11 @@ interface IIndexService {
* @since 15.0.0
*
* @param string $providerId
- * @param $documentId
+ * @param string $documentId
* @param int $status
* @param bool $reset
*/
- public function updateIndexStatus(string $providerId, $documentId, int $status, bool $reset = false);
+ public function updateIndexStatus(string $providerId, string $documentId, int $status, bool $reset = false);
/**
diff --git a/resources/app-info.xsd b/resources/app-info.xsd
index fa06752c01d..287ed6b9913 100644
--- a/resources/app-info.xsd
+++ b/resources/app-info.xsd
@@ -63,6 +63,8 @@
maxOccurs="1" />
<xs:element name="trash" type="trash" minOccurs="0"
maxOccurs="1" />
+ <xs:element name="versions" type="versions" minOccurs="0"
+ maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:unique name="uniqueNameL10n">
@@ -670,6 +672,21 @@
</xs:simpleContent>
</xs:complexType>
+ <xs:complexType name="versions">
+ <xs:sequence>
+ <xs:element name="backend" type="versions-backend" minOccurs="1"
+ maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="versions-backend">
+ <xs:simpleContent>
+ <xs:extension base="php-class">
+ <xs:attribute name="for" type="php-class" use="required"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
<xs:simpleType name="php-class">
<xs:restriction base="xs:string">
<xs:pattern
diff --git a/settings/BackgroundJobs/VerifyUserData.php b/settings/BackgroundJobs/VerifyUserData.php
index b4a60ec8405..56ebadff9c3 100644
--- a/settings/BackgroundJobs/VerifyUserData.php
+++ b/settings/BackgroundJobs/VerifyUserData.php
@@ -63,6 +63,9 @@ class VerifyUserData extends Job {
/** @var string */
private $lookupServerUrl;
+ /** @var IConfig */
+ private $config;
+
/**
* VerifyUserData constructor.
*
@@ -85,6 +88,7 @@ class VerifyUserData extends Job {
$lookupServerUrl = $config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
$this->lookupServerUrl = rtrim($lookupServerUrl, '/');
+ $this->config = $config;
}
/**
diff --git a/tests/lib/Files/Storage/Storage.php b/tests/lib/Files/Storage/Storage.php
index 04aafece2e3..a25a3f74f9e 100644
--- a/tests/lib/Files/Storage/Storage.php
+++ b/tests/lib/Files/Storage/Storage.php
@@ -23,6 +23,7 @@
namespace Test\Files\Storage;
use OC\Files\Cache\Watcher;
+use OCP\Files\Storage\IWriteStreamStorage;
abstract class Storage extends \Test\TestCase {
/**
@@ -628,4 +629,20 @@ abstract class Storage extends \Test\TestCase {
$this->instance->rename('bar.txt.part', 'bar.txt');
$this->assertEquals('bar', $this->instance->file_get_contents('bar.txt'));
}
+
+ public function testWriteStream() {
+ $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
+
+ if (!$this->instance->instanceOfStorage(IWriteStreamStorage::class)) {
+ $this->markTestSkipped('Not a WriteSteamStorage');
+ }
+ /** @var IWriteStreamStorage $storage */
+ $storage = $this->instance;
+
+ $source = fopen($textFile, 'r');
+
+ $storage->writeStream('test.txt', $source);
+ $this->assertTrue($storage->file_exists('test.txt'));
+ $this->assertEquals(file_get_contents($textFile), $storage->file_get_contents('test.txt'));
+ }
}
diff --git a/tests/lib/Files/Stream/CountReadStreamTest.php b/tests/lib/Files/Stream/CountReadStreamTest.php
new file mode 100644
index 00000000000..99291d1644f
--- /dev/null
+++ b/tests/lib/Files/Stream/CountReadStreamTest.php
@@ -0,0 +1,49 @@
+<?php declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.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 Test\Files\Stream;
+
+use OC\Files\Stream\CountReadStream;
+use Test\TestCase;
+
+class CountReadStreamTest extends TestCase {
+ private function getStream($data) {
+ $handle = fopen('php://temp', 'w+');
+ fwrite($handle, $data);
+ rewind($handle);
+ return $handle;
+ }
+
+ public function testBasicCount() {
+ $source = $this->getStream('foo');
+ $stream = CountReadStream::wrap($source, function ($size) {
+ $this->assertEquals(3, $size);
+ });
+ stream_get_contents($stream);
+ }
+
+ public function testLarger() {
+ $stream = CountReadStream::wrap(fopen(__DIR__ . '/../../../data/testimage.mp4', 'r'), function ($size) {
+ $this->assertEquals(383631, $size);
+ });
+ stream_get_contents($stream);
+ }
+}
diff --git a/tests/lib/Preview/GeneratorTest.php b/tests/lib/Preview/GeneratorTest.php
index 64786fa9fe8..565b526b659 100644
--- a/tests/lib/Preview/GeneratorTest.php
+++ b/tests/lib/Preview/GeneratorTest.php
@@ -104,7 +104,7 @@ class GeneratorTest extends \Test\TestCase {
$previewFile = $this->createMock(ISimpleFile::class);
$previewFolder->method('getFile')
- ->with($this->equalTo('128-128.png'))
+ ->with($this->equalTo('256-256.png'))
->willReturn($previewFile);
$this->eventDispatcher->expects($this->once())
@@ -212,7 +212,7 @@ class GeneratorTest extends \Test\TestCase {
->will($this->returnCallback(function($filename) use ($maxPreview, $previewFile) {
if ($filename === '2048-2048-max.png') {
return $maxPreview;
- } else if ($filename === '128-128.png') {
+ } else if ($filename === '256-256.png') {
return $previewFile;
}
$this->fail('Unexpected file');
@@ -223,7 +223,7 @@ class GeneratorTest extends \Test\TestCase {
->with($this->equalTo('my data'));
$previewFolder->method('getFile')
- ->with($this->equalTo('128-128.png'))
+ ->with($this->equalTo('256-256.png'))
->willThrowException(new NotFoundException());
$image = $this->createMock(IImage::class);
@@ -233,7 +233,7 @@ class GeneratorTest extends \Test\TestCase {
$image->expects($this->once())
->method('resize')
- ->with(128);
+ ->with(256);
$image->method('data')
->willReturn('my resized data');
$image->method('valid')->willReturn(true);
@@ -328,8 +328,8 @@ class GeneratorTest extends \Test\TestCase {
return [
[1024, 2048, 512, 512, false, IPreview::MODE_FILL, 256, 512],
[1024, 2048, 512, 512, false, IPreview::MODE_COVER, 512, 1024],
- [1024, 2048, 512, 512, true, IPreview::MODE_FILL, 512, 512],
- [1024, 2048, 512, 512, true, IPreview::MODE_COVER, 512, 512],
+ [1024, 2048, 512, 512, true, IPreview::MODE_FILL, 1024, 1024],
+ [1024, 2048, 512, 512, true, IPreview::MODE_COVER, 1024, 1024],
[1024, 2048, -1, 512, false, IPreview::MODE_COVER, 256, 512],
[1024, 2048, 512, -1, false, IPreview::MODE_FILL, 512, 1024],
@@ -343,14 +343,20 @@ class GeneratorTest extends \Test\TestCase {
[2048, 1024, 512, 512, false, IPreview::MODE_FILL, 512, 256],
[2048, 1024, 512, 512, false, IPreview::MODE_COVER, 1024, 512],
- [2048, 1024, 512, 512, true, IPreview::MODE_FILL, 512, 512],
- [2048, 1024, 512, 512, true, IPreview::MODE_COVER, 512, 512],
+ [2048, 1024, 512, 512, true, IPreview::MODE_FILL, 1024, 1024],
+ [2048, 1024, 512, 512, true, IPreview::MODE_COVER, 1024, 1024],
[2048, 1024, -1, 512, false, IPreview::MODE_FILL, 1024, 512],
[2048, 1024, 512, -1, false, IPreview::MODE_COVER, 512, 256],
[2048, 1024, 4096, 1024, true, IPreview::MODE_FILL, 2048, 512],
[2048, 1024, 4096, 1024, true, IPreview::MODE_COVER, 2048, 512],
+
+ //Test minimum size
+ [2048, 1024, 32, 32, false, IPreview::MODE_FILL, 64, 32],
+ [2048, 1024, 32, 32, false, IPreview::MODE_COVER, 64, 32],
+ [2048, 1024, 32, 32, true, IPreview::MODE_FILL, 64, 64],
+ [2048, 1024, 32, 32, true, IPreview::MODE_COVER, 64, 64],
];
}