diff --git a/appinfo/info.xml b/appinfo/info.xml
index eaf4f9d877..7419dd7571 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -6,7 +6,7 @@
- 5.0.0-beta1
+ 6.0.0-dev.1
agpl
Collabora Productivity based on work of Frank Karlitschek, Victor Dubiniuk
@@ -31,6 +31,7 @@ You can also edit your documents off-line with the Collabora Office app from the
OCA\Richdocuments\Backgroundjobs\ObtainCapabilities
+ OCA\Richdocuments\Backgroundjobs\Cleanup
OCA\Richdocuments\Command\ActivateConfig
diff --git a/emptyTemplates/odttemplate.odt b/emptyTemplates/odttemplate.odt
deleted file mode 100644
index d2a8dc274c..0000000000
Binary files a/emptyTemplates/odttemplate.odt and /dev/null differ
diff --git a/emptyTemplates/docxtemplate.docx b/emptyTemplates/template.docx
similarity index 100%
rename from emptyTemplates/docxtemplate.docx
rename to emptyTemplates/template.docx
diff --git a/emptyTemplates/odgtemplate.otg b/emptyTemplates/template.odg
similarity index 100%
rename from emptyTemplates/odgtemplate.otg
rename to emptyTemplates/template.odg
diff --git a/emptyTemplates/pptxtemplate.pptx b/emptyTemplates/template.pptx
similarity index 100%
rename from emptyTemplates/pptxtemplate.pptx
rename to emptyTemplates/template.pptx
diff --git a/emptyTemplates/xlsxtemplate.xlsx b/emptyTemplates/template.xlsx
similarity index 100%
rename from emptyTemplates/xlsxtemplate.xlsx
rename to emptyTemplates/template.xlsx
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 696fb4e6d1..a8af2239fe 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -31,6 +31,7 @@
use OCA\Richdocuments\AppConfig;
use OCA\Richdocuments\Capabilities;
use OCA\Richdocuments\Middleware\WOPIMiddleware;
+use OCA\Richdocuments\Listener\FileCreatedFromTemplateListener;
use OCA\Richdocuments\PermissionManager;
use OCA\Richdocuments\Preview\MSExcel;
use OCA\Richdocuments\Preview\MSWord;
@@ -48,6 +49,7 @@
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Files\Template\FileCreatedFromTemplateEvent;
use OCP\Files\Template\ITemplateManager;
use OCP\Files\Template\TemplateFileCreator;
use OCP\IConfig;
@@ -68,6 +70,7 @@ public function register(IRegistrationContext $context): void {
$context->registerTemplateProvider(CollaboraTemplateProvider::class);
$context->registerCapability(Capabilities::class);
$context->registerMiddleWare(WOPIMiddleware::class);
+ $context->registerEventListener(FileCreatedFromTemplateEvent::class, FileCreatedFromTemplateListener::class);
}
public function boot(IBootContext $context): void {
diff --git a/lib/Backgroundjobs/Cleanup.php b/lib/Backgroundjobs/Cleanup.php
new file mode 100644
index 0000000000..e5adb55acb
--- /dev/null
+++ b/lib/Backgroundjobs/Cleanup.php
@@ -0,0 +1,49 @@
+
+ *
+ * @author Roeland Jago Douma
+ *
+ * @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 .
+ *
+ */
+
+namespace OCA\Richdocuments\Backgroundjobs;
+
+use OC\BackgroundJob\TimedJob;
+use OCA\Richdocuments\Service\CapabilitiesService;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+
+class Cleanup extends TimedJob {
+
+ /** @var IDBConnection */
+ private $db;
+
+ public function __construct(IDBConnection $db) {
+ $this->db = $db;
+
+ $this->setInterval(60*60);
+ }
+
+ protected function run($argument) {
+ // Expire template mappings for file creation
+ $query = $this->db->getQueryBuilder();
+ $query->delete('richdocuments_template')
+ ->where($query->expr()->lte('timestamp', $query->createNamedParameter(time() - 60, IQueryBuilder::PARAM_INT)));
+ $query->executeStatement();
+ }
+}
diff --git a/lib/Controller/DocumentController.php b/lib/Controller/DocumentController.php
index 9f1f7cec1c..8e1932a20b 100644
--- a/lib/Controller/DocumentController.php
+++ b/lib/Controller/DocumentController.php
@@ -11,6 +11,7 @@
namespace OCA\Richdocuments\Controller;
+use OCA\Richdocuments\AppInfo\Application;
use OCA\Richdocuments\Events\BeforeFederationRedirectEvent;
use OCA\Richdocuments\Service\FederationService;
use OCA\Richdocuments\Service\InitialStateService;
@@ -210,7 +211,14 @@ public function index($fileId, $path = null) {
return $response;
}
- list($urlSrc, $token, $wopi) = $this->tokenManager->getToken($item->getId());
+ $templateFile = $this->templateManager->getTemplateSource($item->getId());
+ if ($templateFile) {
+ list($urlSrc, $wopi) = $this->tokenManager->getTokenForTemplate($templateFile, $this->uid, $item->getId());
+ $token = $wopi->getToken();
+ } else {
+ list($urlSrc, $token, $wopi) = $this->tokenManager->getToken($item->getId());
+ }
+
$params = [
'permissions' => $item->getPermissions(),
'title' => $item->getName(),
@@ -575,6 +583,7 @@ public function create($mimetype,
}
if (!$content){
+ // FIXME: see if this is used,
$content = file_get_contents(dirname(dirname(__DIR__)) . self::ODT_TEMPLATE_PATH);
}
diff --git a/lib/Listener/FileCreatedFromTemplateListener.php b/lib/Listener/FileCreatedFromTemplateListener.php
new file mode 100644
index 0000000000..6cc4d472bc
--- /dev/null
+++ b/lib/Listener/FileCreatedFromTemplateListener.php
@@ -0,0 +1,71 @@
+
+ *
+ * @author Julius Härtl
+ *
+ * @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 .
+ *
+ */
+
+declare(strict_types=1);
+
+
+namespace OCA\Richdocuments\Listener;
+
+
+use OCA\Richdocuments\AppInfo\Application;
+use OCA\Richdocuments\TemplateManager;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\Files\Template\FileCreatedFromTemplateEvent;
+use OCP\IConfig;
+
+class FileCreatedFromTemplateListener implements IEventListener {
+
+ /** @var TemplateManager */
+ private $templateManager;
+
+ public function __construct(
+ TemplateManager $templateManager
+ ) {
+ $this->templateManager = $templateManager;
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof FileCreatedFromTemplateEvent)) {
+ return;
+ }
+
+ $templateFile = $event->getTemplate();
+
+ // Empty template
+ if ($templateFile === null) {
+ $event->getTarget()->putContent($this->templateManager->getEmptyFileContent($event->getTarget()->getExtension()));
+ return;
+ }
+
+ if ($this->templateManager->isSupportedTemplateSource($templateFile->getExtension())) {
+ // Only use TemplateSource if supported filetype
+ $this->templateManager->setTemplateSource($event->getTarget()->getId(), $templateFile->getId());
+ }
+
+ // Avoid having the mimetype of the source file set
+ $event->getTarget()->getStorage()->getCache()->update($event->getTarget()->getId(), [
+ 'mimetype' => $event->getTarget()->getMimeType()
+ ]);
+ }
+}
diff --git a/lib/Migration/Version50200Date20211220212457.php b/lib/Migration/Version50200Date20211220212457.php
new file mode 100644
index 0000000000..5eb408c12f
--- /dev/null
+++ b/lib/Migration/Version50200Date20211220212457.php
@@ -0,0 +1,51 @@
+hasTable('richdocuments_template')) {
+ $table = $schema->createTable('richdocuments_template');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 20,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('userid', 'string', [
+ 'notnull' => false,
+ 'length' => 64,
+ ]);
+ $table->addColumn('fileid', 'bigint', [
+ 'notnull' => true,
+ 'length' => 20,
+ ]);
+ $table->addColumn('templateid', 'bigint', [
+ 'notnull' => true,
+ 'length' => 20,
+ ]);
+ $table->addColumn('timestamp', 'bigint', [
+ 'notnull' => true,
+ 'length' => 20,
+ 'unsigned' => true,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addUniqueIndex(['userid', 'fileid'], 'rd_t_user_file');
+ }
+
+ return $schema;
+ }
+}
diff --git a/lib/Template/CollaboraTemplateProvider.php b/lib/Template/CollaboraTemplateProvider.php
index 4246f98b80..aea8bd8b91 100644
--- a/lib/Template/CollaboraTemplateProvider.php
+++ b/lib/Template/CollaboraTemplateProvider.php
@@ -73,8 +73,4 @@ public function getCustomTemplates(string $mimetype): array {
public function getCustomTemplate(string $template): File {
return $this->templateManager->get((int)$template);
}
-
- public function createFromTemplate(File $template, File $target): void {
- // TODO: Implement createFromTemplate() method.
- }
}
diff --git a/lib/TemplateManager.php b/lib/TemplateManager.php
index a907f9aa76..39dcbd2417 100644
--- a/lib/TemplateManager.php
+++ b/lib/TemplateManager.php
@@ -24,6 +24,8 @@
namespace OCA\Richdocuments;
+use OCA\Richdocuments\AppInfo\Application;
+use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IAppData;
@@ -31,16 +33,14 @@
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\IConfig;
+use OCP\IDBConnection;
use OCP\IL10N;
-use OCP\IPreview;
use OCP\IURLGenerator;
-use OC\Files\AppData\Factory;
+use Psr\Log\LoggerInterface;
+use Throwable;
class TemplateManager {
- /** @var string */
- protected $appName;
-
/** @var string */
protected $userId;
@@ -56,6 +56,15 @@ class TemplateManager {
/** @var IL10N */
private $l;
+ /** @var IDBConnection */
+ private $db;
+
+ /** @var IAppData */
+ private $appData;
+
+ /** @var LoggerInterface */
+ private $logger;
+
/** Accepted templates mime types */
const MIMES_DOCUMENTS = [
'application/vnd.oasis.opendocument.text-template',
@@ -97,39 +106,27 @@ class TemplateManager {
'presentation' => 'pptx',
];
- /**
- * Template manager
- *
- * @param string $appName
- * @param string $userId
- * @param IConfig $config
- * @param Factory $appDataFactory
- * @param IURLGenerator $urlGenerator
- * @param IRootFolder $rootFolder
- * @param IL10N $l
- * @throws \OCP\Files\NotPermittedException
- */
- public function __construct($appName,
- $userId,
- IConfig $config,
- IAppData $appData,
- IURLGenerator $urlGenerator,
- IRootFolder $rootFolder,
- IL10N $l) {
- $this->appName = $appName;
- $this->userId = $userId;
- $this->config = $config;
- $this->rootFolder = $rootFolder;
- $this->urlGenerator = $urlGenerator;
-
-
+ public function __construct(
+ $userId,
+ IConfig $config,
+ IAppData $appData,
+ IURLGenerator $urlGenerator,
+ IRootFolder $rootFolder,
+ IL10N $l,
+ IDBConnection $connection,
+ LoggerInterface $logger
+ ) {
+ $this->userId = $userId;
+ $this->config = $config;
+ $this->rootFolder = $rootFolder;
+ $this->urlGenerator = $urlGenerator;
+ $this->db = $connection;
+ $this->logger = $logger;
$this->appData = $appData;
- $this->createAppDataFolders();
-
$this->l = $l;
}
- private function createAppDataFolders() {
+ private function ensureAppDataFolders() {
/*
* Init the appdata folder
* We need an actual folder for the fileid and previews.
@@ -200,7 +197,7 @@ private function filterTemplates($templates, $type = null) {
});
}
- private function getEmpty($type = null) {
+ public function getEmpty($type = null) {
$folder = $this->getEmptyTemplateDir();
$templateFiles = $folder->getDirectoryListing();
@@ -228,6 +225,7 @@ private function getEmpty($type = null) {
* Remove empty_templates in appdata and recreate it from the apps templates
*/
public function updateEmptyTemplates() {
+ $this->ensureAppDataFolders();
try {
$folder = $this->getEmptyTemplateDir();
$folder->delete();
@@ -393,7 +391,7 @@ private function getUserTemplateDir() {
}
// has the user manually set a directory as the default template dir ?
- $templateDirPath = $this->config->getUserValue($this->userId, $this->appName, 'templateFolder', false);
+ $templateDirPath = $this->config->getUserValue($this->userId, Application::APPNAME, 'templateFolder', false);
$userFolder = $this->rootFolder->getUserFolder($this->userId);
if ($templateDirPath !== false) {
@@ -418,6 +416,7 @@ private function getUserTemplateDir() {
* @return Folder
*/
private function getSystemTemplateDir() {
+ $this->ensureAppDataFolders();
$path = 'appdata_' . $this->config->getSystemValue('instanceid', null) . '/richdocuments/templates';
return $this->rootFolder->get($path);
}
@@ -426,6 +425,7 @@ private function getSystemTemplateDir() {
* @return Folder
*/
private function getEmptyTemplateDir() {
+ $this->ensureAppDataFolders();
$path = 'appdata_' . $this->config->getSystemValue('instanceid', null) . '/richdocuments/empty_templates';
return $this->rootFolder->get($path);
}
@@ -437,7 +437,7 @@ private function getEmptyTemplateDir() {
* @return array
*/
public function formatNodeReturn(File $template) {
- $ooxml = $this->config->getAppValue($this->appName, 'doc_format', '') === 'ooxml';
+ $ooxml = $this->config->getAppValue(Application::APPNAME, 'doc_format', '') === 'ooxml';
$documentType = $this->flipTypes()[$template->getMimeType()];
return [
'id' => $template->getId(),
@@ -466,7 +466,7 @@ public function isTemplate($fileId) {
}
public function formatEmpty(File $template) {
- $ooxml = $this->config->getAppValue($this->appName, 'doc_format', '') === 'ooxml';
+ $ooxml = $this->config->getAppValue(Application::APPNAME, 'doc_format', '') === 'ooxml';
$documentType = $this->flipTypes()[$template->getMimeType()];
return [
'id' => $template->getId(),
@@ -490,4 +490,79 @@ public function isValidTemplateMime($mime, $type = null) {
return true;
}
+
+ /**
+ * Return default content for empty files of a given filename by file extension
+ */
+ public function getEmptyFileContent(string $extension): string {
+ $supportedExtensions = ['odt', 'ods', 'odp', 'odg', 'docx', 'xlsx', 'pptx'];
+ $emptyPath = __DIR__ . '/../emptyTemplates/template.' . $extension;
+
+ if (in_array($extension, $supportedExtensions, true) && file_exists($emptyPath)) {
+ return file_get_contents($emptyPath);
+ }
+
+ return '';
+ }
+
+ public function isSupportedTemplateSource(string $extension): bool {
+ $supportedExtensions = ['ott', 'otg', 'otp', 'ots'];
+ return in_array($extension, $supportedExtensions, true);
+ }
+
+ public function setTemplateSource(int $fileId, int $templateId): void {
+ try {
+ $query = $this->db->getQueryBuilder();
+ $query->insert('richdocuments_template')
+ ->values([
+ 'userid' => $query->createNamedParameter($this->userId),
+ 'fileid' => $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT),
+ 'templateid' => $query->createNamedParameter($templateId, IQueryBuilder::PARAM_INT),
+ 'timestamp' => $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT)
+ ]);
+ $query->executeStatement();
+ } catch (Throwable $e) {
+ $this->logger->warning('Could not store template source', ['exception' => $e]);
+ // Ignore failure and proceed with empty template
+ }
+ }
+
+ public function getTemplateSource(int $fileId): ?File {
+ $templateId = 0;
+ try {
+ $query = $this->db->getQueryBuilder();
+ $query->select('templateid')
+ ->from('richdocuments_template')
+ ->where($query->expr()->eq('userid', $query->createNamedParameter($this->userId)))
+ ->andWhere($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
+ $result = $query->executeQuery();
+ $templateId = (int)$result->fetchOne();
+
+ $query->delete('richdocuments_template')
+ ->where($query->expr()->eq('userid', $query->createNamedParameter($this->userId)))
+ ->andWhere($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
+ $query->executeStatement();
+ } catch (Throwable $e) {
+ // Ignore failure and proceed with empty template
+ $this->logger->warning('Could not retrieve template source', ['exception' => $e]);
+ return null;
+ }
+
+ if ($templateId !== 0) {
+ try {
+ $template = $this->get($templateId);
+ } catch (NotFoundException $e) {
+ $userFolder = $this->rootFolder->getUserFolder($this->userId);
+ try {
+ $template = $userFolder->getById($templateId);
+ } catch (NotFoundException $e) {
+ $this->logger->warning('Could not retrieve template source file', ['exception' => $e]);
+ return null;
+ }
+ }
+ return $template;
+ }
+
+ return null;
+ }
}