mirror of
https://github.com/nextcloud/richdocuments.git
synced 2025-12-17 21:12:14 +01:00
Use FileCreatedFromTemplateEvent to inject the already existing empty template files for Collabora
Signed-off-by: Julius Härtl <jus@bitgrid.net> Cleanup template loading Signed-off-by: Julius Härtl <jus@bitgrid.net> Fix template handling Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
parent
03d939e0c4
commit
d491bfa9ff
13 changed files with 299 additions and 44 deletions
|
|
@ -6,7 +6,7 @@
|
|||
<description><** and **[iOS](https://apps.apple.com/us/app/collabora-office/id1440482071)** store.]]></description>
|
||||
<version>5.0.0-beta1</version>
|
||||
<version>6.0.0-dev.1</version>
|
||||
<licence>agpl</licence>
|
||||
<author>Collabora Productivity based on work of Frank Karlitschek, Victor Dubiniuk</author>
|
||||
<types>
|
||||
|
|
@ -31,6 +31,7 @@ You can also edit your documents off-line with the Collabora Office app from the
|
|||
</dependencies>
|
||||
<background-jobs>
|
||||
<job>OCA\Richdocuments\Backgroundjobs\ObtainCapabilities</job>
|
||||
<job>OCA\Richdocuments\Backgroundjobs\Cleanup</job>
|
||||
</background-jobs>
|
||||
<commands>
|
||||
<command>OCA\Richdocuments\Command\ActivateConfig</command>
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -31,6 +31,7 @@ use OCA\Files_Sharing\Listener\LoadAdditionalListener;
|
|||
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\IBootContext;
|
|||
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 @@ class Application extends App implements IBootstrap {
|
|||
$context->registerTemplateProvider(CollaboraTemplateProvider::class);
|
||||
$context->registerCapability(Capabilities::class);
|
||||
$context->registerMiddleWare(WOPIMiddleware::class);
|
||||
$context->registerEventListener(FileCreatedFromTemplateEvent::class, FileCreatedFromTemplateListener::class);
|
||||
}
|
||||
|
||||
public function boot(IBootContext $context): void {
|
||||
|
|
|
|||
49
lib/Backgroundjobs/Cleanup.php
Normal file
49
lib/Backgroundjobs/Cleanup.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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 @@ class DocumentController extends Controller {
|
|||
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 @@ class DocumentController extends Controller {
|
|||
}
|
||||
|
||||
if (!$content){
|
||||
// FIXME: see if this is used,
|
||||
$content = file_get_contents(dirname(dirname(__DIR__)) . self::ODT_TEMPLATE_PATH);
|
||||
}
|
||||
|
||||
|
|
|
|||
71
lib/Listener/FileCreatedFromTemplateListener.php
Normal file
71
lib/Listener/FileCreatedFromTemplateListener.php
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
/*
|
||||
* @copyright Copyright (c) 2021 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
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()
|
||||
]);
|
||||
}
|
||||
}
|
||||
51
lib/Migration/Version50200Date20211220212457.php
Normal file
51
lib/Migration/Version50200Date20211220212457.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Richdocuments\Migration;
|
||||
|
||||
use Closure;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
/**
|
||||
* Auto-generated migration step: Please modify to your needs!
|
||||
*/
|
||||
class Version50200Date20211220212457 extends SimpleMigrationStep {
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
if (!$schema->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;
|
||||
}
|
||||
}
|
||||
|
|
@ -91,8 +91,4 @@ class CollaboraTemplateProvider implements ICustomTemplateProvider {
|
|||
public function getCustomTemplate(string $template): File {
|
||||
return $this->templateManager->get((int)$template);
|
||||
}
|
||||
|
||||
public function createFromTemplate(File $template, File $target): void {
|
||||
// TODO: Implement createFromTemplate() method.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ declare (strict_types = 1);
|
|||
|
||||
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\IRootFolder;
|
|||
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 @@ class TemplateManager {
|
|||
});
|
||||
}
|
||||
|
||||
private function getEmpty($type = null) {
|
||||
public function getEmpty($type = null) {
|
||||
$folder = $this->getEmptyTemplateDir();
|
||||
|
||||
$templateFiles = $folder->getDirectoryListing();
|
||||
|
|
@ -228,6 +225,7 @@ class TemplateManager {
|
|||
* 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 @@ class TemplateManager {
|
|||
}
|
||||
|
||||
// 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 @@ class TemplateManager {
|
|||
* @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 @@ class TemplateManager {
|
|||
* @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 @@ class TemplateManager {
|
|||
* @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 @@ class TemplateManager {
|
|||
}
|
||||
|
||||
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 @@ class TemplateManager {
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue