Clean repo layout: patches/ + install.sh only

This commit is contained in:
Hermes
2026-09-11 12:19:56 +00:00
parent 88d618b58a
commit 84d455d19c
10 changed files with 0 additions and 639 deletions
-58
View File
@@ -1,58 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2026 HtmlViewer / patch by Hermes Agent
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* Streams the raw HTML file for the DirectEditing sandbox iframe.
* Authenticated via the user session (direct editing keeps a session).
*/
namespace OCA\HtmlViewer\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\Files\File;
use OCP\Files\IRootFolder;
use OCP\IRequest;
use OCP\IUserSession;
class DirectController extends Controller {
public function __construct(
string $appName,
IRequest $request,
private IRootFolder $rootFolder,
private IUserSession $userSession,
) {
parent::__construct($appName, $request);
}
#[NoAdminRequired]
#[NoCSRFRequired]
public function content(int $fileId) {
$user = $this->userSession->getUser();
if ($user === null) {
return new NotFoundResponse();
}
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
$nodes = $userFolder->getById($fileId);
if (empty($nodes)) {
return new NotFoundResponse();
}
$file = array_shift($nodes);
if (!$file instanceof File) {
return new NotFoundResponse();
}
$response = new DataDisplayResponse($file->getContent());
$response->addHeader('Content-Type', 'text/html; charset=utf-8');
return $response;
}
}
-88
View File
@@ -1,88 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2026 HtmlViewer / patch by Hermes Agent
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* DirectEditing editor for text/html so the Nextcloud iOS/Android apps
* open HTML files in an embedded WebView instead of falling back to
* QuickLook source display.
*/
namespace OCA\HtmlViewer\DirectEditing;
use OCA\HtmlViewer\AppInfo\Application;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\DirectEditing\IEditor;
use OCP\DirectEditing\IToken;
use OCP\Files\InvalidPathException;
use OCP\Files\NotFoundException;
use OCP\IL10N;
use OCP\IURLGenerator;
class HtmlDirectEditor implements IEditor {
public function __construct(
private IL10N $l10n,
private IURLGenerator $urlGenerator,
) {
}
#[\Override]
public function getId(): string {
return Application::APP_ID;
}
#[\Override]
public function getName(): string {
return $this->l10n->t('HTML Viewer');
}
#[\Override]
public function getMimetypes(): array {
return [
'text/html',
];
}
#[\Override]
public function getMimetypesOptional(): array {
return [];
}
#[\Override]
public function getCreators(): array {
return [];
}
#[\Override]
public function isSecure(): bool {
return false;
}
#[\Override]
public function open(IToken $token): Response {
$token->useTokenScope();
try {
$file = $token->getFile();
return new TemplateResponse(
Application::APP_ID,
'directEditing',
[
'fileId' => $file->getId(),
'fileName' => $file->getName(),
'fileContent' => $file->getContent(),
],
'base'
);
} catch (InvalidPathException|NotFoundException) {
return new NotFoundResponse();
}
}
}
-44
View File
@@ -1,44 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2026 HtmlViewer / patch by Hermes Agent
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* Bridge called from OCA\Text\DirectEditing\TextDirectEditor::open()
* for text/html files. Reuses the htmlviewer DirectEditing template
* so the mobile apps (hard-coded editor registry) render HTML in a
* sandboxed iframe instead of showing source via QuickLook.
*/
namespace OCA\HtmlViewer\DirectEditing;
use OCA\HtmlViewer\AppInfo\Application;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\DirectEditing\IToken;
use OCP\Server;
class HtmlDirectEditorBridge {
public static function openHtml(IToken $token): Response {
try {
$file = $token->getFile();
return new TemplateResponse(
Application::APP_ID,
'directEditing',
[
'fileId' => $file->getId(),
'fileName' => $file->getName(),
'fileContent' => $file->getContent(),
],
'base'
);
} catch (\Throwable) {
return new NotFoundResponse();
}
}
}
-32
View File
@@ -1,32 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2026 HtmlViewer / patch by Hermes Agent
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\HtmlViewer\Listener;
use OCA\HtmlViewer\DirectEditing\HtmlDirectEditor;
use OCP\DirectEditing\RegisterDirectEditorEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
/** @template-implements IEventListener<Event|RegisterDirectEditorEvent> */
final class RegisterDirectEditorListener implements IEventListener {
public function __construct(
private HtmlDirectEditor $editor,
) {
}
#[\Override]
public function handle(Event $event): void {
if (!$event instanceof RegisterDirectEditorEvent) {
return;
}
$event->register($this->editor);
}
}
-171
View File
@@ -1,171 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Text\DirectEditing;
use OCA\Text\AppInfo\Application;
use OCA\Text\Service\ApiService;
use OCA\Text\Service\InitialStateProvider;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\DirectEditing\IEditor;
use OCP\DirectEditing\IToken;
use OCP\Files\InvalidPathException;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IAppConfig;
use OCP\IL10N;
use OCP\Util;
class TextDirectEditor implements IEditor {
/** @var IL10N */
private $l10n;
/** @var InitialStateProvider */
private $initialStateProvider;
/** @var ApiService */
private $apiService;
/**
* @var IAppConfig
*/
private $appConfig;
public function __construct(IL10N $l10n, InitialStateProvider $initialStateProvider, ApiService $apiService, IAppConfig $appConfig) {
$this->l10n = $l10n;
$this->initialStateProvider = $initialStateProvider;
$this->apiService = $apiService;
$this->appConfig = $appConfig;
}
/**
* Return a unique identifier for the editor
*
* e.g. richdocuments
*
* @return string
*/
public function getId(): string {
return Application::APP_NAME;
}
/**
* Return a readable name for the editor
*
* e.g. Collabora Online
*
* @return string
*/
public function getName(): string {
return $this->l10n->t('Nextcloud Text');
}
/**
* A list of mimetypes that should open the editor by default
*
* @return string[]
*/
public function getMimetypes(): array {
return [
'text/markdown',
'text/plain',
'application/cmd',
'application/x-empty',
'application/x-msdos-program',
'application/javascript',
'application/json',
'application/x-perl',
'application/x-php',
'application/x-tex',
'application/xml',
'application/yaml',
'text/css',
'text/csv',
'text/org',
'text/x-c',
'text/x-c++src',
'text/x-h',
'text/x-java-source',
'text/x-ldif',
'text/x-nfo',
'text/x-python',
'text/x-shellscript',
];
}
/**
* A list of mimetypes that can be opened in the editor optionally
*
* @return string[]
*/
public function getMimetypesOptional(): array {
return [];
}
/**
* Return a list of file creation options to be presented to the user
*
* @return TextDocumentCreator[]
*/
public function getCreators(): array {
return [
new TextDocumentCreator($this->l10n, $this->appConfig),
];
}
/**
* Return if the view is able to securely view a file without downloading it to the browser
*
* @return bool
*/
public function isSecure(): bool {
return false;
}
/**
* Return a template response for displaying the editor
*
* open can only be called once when the client requests the editor with a one-time-use token
* For handling editing and later requests, editors need to impelement their own token handling and take care of invalidation
*
* This behavior is similar to the current direct editing implementation in collabora where we generate a one-time token and switch over to the regular wopi token for the actual editing/saving process
*
* @param IToken $token
* @return Response
*/
public function open(IToken $token): Response {
$token->useTokenScope();
try {
// HTML patch (Hermes Agent 2026-09): route text/html to the
// htmlviewer sandbox template, because the iOS/Android apps
// only know the hard-coded editor id "text" and would
// otherwise show HTML source in QuickLook.
if ($token->getFile()->getMimeType() === 'text/html') {
return \OCA\HtmlViewer\DirectEditing\HtmlDirectEditorBridge::openHtml($token);
}
$session = $this->apiService->create($token->getFile()->getId());
$this->initialStateProvider->provideFile([
'fileId' => $token->getFile()->getId(),
'mimetype' => $token->getFile()->getMimeType(),
'session' => \json_encode($session->getData())
]);
$this->initialStateProvider->provideDirectEditToken($token->getToken());
$this->initialStateProvider->provideState();
Util::addScript('text', 'text-text');
Util::addStyle('text', 'text-text');
return new TemplateResponse('text', 'main', [], 'base');
} catch (InvalidPathException $e) {
} catch (NotFoundException $e) {
} catch (NotPermittedException $e) {
}
return new NotFoundResponse();
}
}
-164
View File
@@ -1,164 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Text\DirectEditing;
use OCA\Text\AppInfo\Application;
use OCA\Text\Service\ApiService;
use OCA\Text\Service\InitialStateProvider;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\DirectEditing\IEditor;
use OCP\DirectEditing\IToken;
use OCP\Files\InvalidPathException;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IAppConfig;
use OCP\IL10N;
use OCP\Util;
class TextDirectEditor implements IEditor {
/** @var IL10N */
private $l10n;
/** @var InitialStateProvider */
private $initialStateProvider;
/** @var ApiService */
private $apiService;
/**
* @var IAppConfig
*/
private $appConfig;
public function __construct(IL10N $l10n, InitialStateProvider $initialStateProvider, ApiService $apiService, IAppConfig $appConfig) {
$this->l10n = $l10n;
$this->initialStateProvider = $initialStateProvider;
$this->apiService = $apiService;
$this->appConfig = $appConfig;
}
/**
* Return a unique identifier for the editor
*
* e.g. richdocuments
*
* @return string
*/
public function getId(): string {
return Application::APP_NAME;
}
/**
* Return a readable name for the editor
*
* e.g. Collabora Online
*
* @return string
*/
public function getName(): string {
return $this->l10n->t('Nextcloud Text');
}
/**
* A list of mimetypes that should open the editor by default
*
* @return string[]
*/
public function getMimetypes(): array {
return [
'text/markdown',
'text/plain',
'application/cmd',
'application/x-empty',
'application/x-msdos-program',
'application/javascript',
'application/json',
'application/x-perl',
'application/x-php',
'application/x-tex',
'application/xml',
'application/yaml',
'text/css',
'text/csv',
'text/html',
'text/org',
'text/x-c',
'text/x-c++src',
'text/x-h',
'text/x-java-source',
'text/x-ldif',
'text/x-nfo',
'text/x-python',
'text/x-shellscript',
];
}
/**
* A list of mimetypes that can be opened in the editor optionally
*
* @return string[]
*/
public function getMimetypesOptional(): array {
return [];
}
/**
* Return a list of file creation options to be presented to the user
*
* @return TextDocumentCreator[]
*/
public function getCreators(): array {
return [
new TextDocumentCreator($this->l10n, $this->appConfig),
];
}
/**
* Return if the view is able to securely view a file without downloading it to the browser
*
* @return bool
*/
public function isSecure(): bool {
return false;
}
/**
* Return a template response for displaying the editor
*
* open can only be called once when the client requests the editor with a one-time-use token
* For handling editing and later requests, editors need to impelement their own token handling and take care of invalidation
*
* This behavior is similar to the current direct editing implementation in collabora where we generate a one-time token and switch over to the regular wopi token for the actual editing/saving process
*
* @param IToken $token
* @return Response
*/
public function open(IToken $token): Response {
$token->useTokenScope();
try {
$session = $this->apiService->create($token->getFile()->getId());
$this->initialStateProvider->provideFile([
'fileId' => $token->getFile()->getId(),
'mimetype' => $token->getFile()->getMimeType(),
'session' => \json_encode($session->getData())
]);
$this->initialStateProvider->provideDirectEditToken($token->getToken());
$this->initialStateProvider->provideState();
Util::addScript('text', 'text-text');
Util::addStyle('text', 'text-text');
return new TemplateResponse('text', 'main', [], 'base');
} catch (InvalidPathException $e) {
} catch (NotFoundException $e) {
} catch (NotPermittedException $e) {
}
return new NotFoundResponse();
}
}
-37
View File
@@ -1,37 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2026 HtmlViewer / patch by Hermes Agent
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* DirectEditing template: renders the HTML file in a sandboxed iframe
* that fills the whole viewport (works in the iOS/Android WebView).
* The file content is embedded as srcdoc, so the iframe needs no
* session cookie (mobile WebViews do not share the app session).
*/
/** @var array $_ */
?>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
background-color: var(--color-main-background, #ffffff);
}
#htmlviewer-frame {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
background: transparent;
}
</style>
<iframe
id="htmlviewer-frame"
title="<?php echo htmlspecialchars($_['fileName'] ?? 'HTML'); ?>"
srcdoc="<?php echo htmlspecialchars($_['fileContent'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8', false); ?>"
sandbox="allow-same-origin allow-scripts allow-popups allow-popups-to-escape-sandbox allow-modals"
></iframe>
-26
View File
@@ -1,26 +0,0 @@
import re
APP='/media/orange/RocketChat/nextcloud1/nextcloud_data/custom_apps/htmlviewer'
p=APP+'/lib/AppInfo/Application.php'
c=open(p).read()
if 'RegisterDirectEditorListener' not in c:
c=c.replace(
'use OCP\\Security\\CSP\\AddContentSecurityPolicyEvent;',
'use OCP\\Security\\CSP\\AddContentSecurityPolicyEvent;\nuse OCA\\HtmlViewer\\Listener\\RegisterDirectEditorListener;\nuse OCP\\DirectEditing\\RegisterDirectEditorEvent;'
)
c=c.replace(
" $context->registerEventListener(AddContentSecurityPolicyEvent::class, CSPListener::class);",
" $context->registerEventListener(AddContentSecurityPolicyEvent::class, CSPListener::class);\n $context->registerEventListener(RegisterDirectEditorEvent::class, RegisterDirectEditorListener::class);"
)
open(p,'w').write(c)
print('Application.php patched:', 'RegisterDirectEditorEvent' in open(p).read())
p=APP+'/appinfo/routes.php'
c=open(p).read()
if 'Direct#content' not in c:
c=c.replace(
"['name' => 'settings#disableWarning', 'url' => '/settings/warning', 'verb' => 'GET'],",
"['name' => 'settings#disableWarning', 'url' => '/settings/warning', 'verb' => 'GET'],\n\t\t['name' => 'Direct#content', 'url' => '/direct/{fileId}', 'verb' => 'GET'],"
)
open(p,'w').write(c)
print('routes.php patched:', 'Direct#content' in open(p).read())
-10
View File
@@ -1,10 +0,0 @@
<?php
echo "start\n";
require "/var/www/html/lib/base.php";
echo "base ok\n";
$m = \OC::server->get(\OCP\DirectEditing\IManager::class);
echo "manager ok\n";
$ev = new \OCP\DirectEditing\RegisterDirectEditorEvent($m);
\OC::server->get(\OCP\EventDispatcher\IEventDispatcher::class)->dispatchTyped($ev);
echo "dispatched\n";
var_dump(array_keys($m->getEditors()));
-9
View File
@@ -1,9 +0,0 @@
<?php
$_SERVER['HTTP_USER_AGENT']='test';
define('PHPUNIT_RUN', true);
$_SERVER['REQUEST_URI']='/index.php';
require "/var/www/html/lib/base.php";
$m = \OC::$server->get(\OCP\DirectEditing\IManager::class);
$ev = new \OCP\DirectEditing\RegisterDirectEditorEvent($m);
\OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class)->dispatchTyped($ev);
echo json_encode(array_keys($m->getEditors())), PHP_EOL;