Skip to content

Commit 69a4fc3

Browse files
committed
feat: add support for comments in forms
Signed-off-by: Christian Hartmann <chris-hartmann@gmx.de>
1 parent 30d57bc commit 69a4fc3

23 files changed

Lines changed: 364 additions & 19 deletions

lib/AppInfo/Application.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
use OCA\Forms\Capabilities;
1414
use OCA\Forms\FormsMigrator;
1515
use OCA\Forms\Listener\AnalyticsDatasourceListener;
16+
use OCA\Forms\Listener\CommentsEntityListener;
1617
use OCA\Forms\Listener\UserDeletedListener;
1718
use OCA\Forms\Middleware\ThrottleFormAccessMiddleware;
1819
use OCA\Forms\Search\SearchProvider;
1920
use OCP\AppFramework\App;
2021
use OCP\AppFramework\Bootstrap\IBootContext;
2122
use OCP\AppFramework\Bootstrap\IBootstrap;
2223
use OCP\AppFramework\Bootstrap\IRegistrationContext;
24+
use OCP\Comments\CommentsEntityEvent;
2325
use OCP\User\Events\UserDeletedEvent;
2426

2527
class Application extends App implements IBootstrap {
@@ -44,6 +46,7 @@ public function register(IRegistrationContext $context): void {
4446
$context->registerCapability(Capabilities::class);
4547
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
4648
$context->registerEventListener(DatasourceEvent::class, AnalyticsDatasourceListener::class);
49+
$context->registerEventListener(CommentsEntityEvent::class, CommentsEntityListener::class);
4750
$context->registerMiddleware(ThrottleFormAccessMiddleware::class);
4851
$context->registerSearchProvider(SearchProvider::class);
4952
$context->registerUserMigrator(FormsMigrator::class);

lib/Constants.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class Constants {
2020
public const CONFIG_KEY_RESTRICTCREATION = 'restrictCreation';
2121
public const CONFIG_KEY_ALLOWCONFIRMATIONEMAIL = 'allowConfirmationEmail';
2222
public const CONFIG_KEY_CONFIRMATIONEMAILRATELIMIT = 'confirmationEmailRateLimit';
23+
public const CONFIG_KEY_ALLOWCOMMENTS = 'allowComments';
2324
public const CONFIG_KEYS = [
2425
self::CONFIG_KEY_ALLOWPERMITALL,
2526
self::CONFIG_KEY_ALLOWPUBLICLINK,
@@ -28,6 +29,7 @@ class Constants {
2829
self::CONFIG_KEY_RESTRICTCREATION,
2930
self::CONFIG_KEY_ALLOWCONFIRMATIONEMAIL,
3031
self::CONFIG_KEY_CONFIRMATIONEMAILRATELIMIT,
32+
self::CONFIG_KEY_ALLOWCOMMENTS,
3133
];
3234

3335
/**

lib/Controller/PageController.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use OCP\AppFramework\Http\Template\PublicTemplateResponse;
3030
use OCP\AppFramework\Http\TemplateResponse;
3131
use OCP\AppFramework\Services\IInitialState;
32+
use OCP\Comments\ICommentsManager;
3233
use OCP\IL10N;
3334
use OCP\IRequest;
3435
use OCP\IURLGenerator;
@@ -51,6 +52,7 @@ public function __construct(
5152
private FormsService $formsService,
5253
private IAccountManager $accountManager,
5354
private IInitialState $initialState,
55+
private ICommentsManager $commentsManager,
5456
private IL10N $l10n,
5557
private IUrlGenerator $urlGenerator,
5658
private IUserManager $userManager,
@@ -66,7 +68,9 @@ public function __construct(
6668
#[NoCSRFRequired()]
6769
#[FrontpageRoute(verb: 'GET', url: '/')]
6870
public function index(?string $hash = null, ?int $submissionId = null): TemplateResponse {
69-
Util::addScript($this->appName, 'forms-main');
71+
// Ensure the Comments client is available and load comments resources
72+
$this->commentsManager->load();
73+
Util::addScript($this->appName, 'forms-main', 'comments');
7074
Util::addStyle($this->appName, 'forms');
7175
Util::addStyle($this->appName, 'forms-style');
7276
$this->insertHeaderOnIos();

lib/Db/Form.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
* @method void setConfirmationEmailBody(string|null $value)
6262
* @method int|null getConfirmationEmailQuestionId()
6363
* @method void setConfirmationEmailQuestionId(int|null $value)
64+
* @method bool getAllowComments()
65+
* @method void setAllowComments(bool $value)
6466
*/
6567
class Form extends Entity {
6668
protected $hash;
@@ -86,6 +88,7 @@ class Form extends Entity {
8688
protected $confirmationEmailSubject;
8789
protected $confirmationEmailBody;
8890
protected $confirmationEmailQuestionId;
91+
protected $allowComments;
8992

9093
/**
9194
* Form constructor.
@@ -104,6 +107,7 @@ public function __construct() {
104107
$this->addType('maxSubmissions', 'integer');
105108
$this->addType('confirmationEmailEnabled', 'boolean');
106109
$this->addType('confirmationEmailQuestionId', 'integer');
110+
$this->addType('allowComments', 'boolean');
107111
}
108112

109113
// JSON-Decoding of access-column.
@@ -182,6 +186,7 @@ public function setAccess(array $access): void {
182186
* confirmationEmailSubject: ?string,
183187
* confirmationEmailBody: ?string,
184188
* confirmationEmailQuestionId: ?int,
189+
* allowComments: bool,
185190
* }
186191
*/
187192
public function read() {
@@ -210,6 +215,7 @@ public function read() {
210215
'confirmationEmailSubject' => $this->getConfirmationEmailSubject(),
211216
'confirmationEmailBody' => $this->getConfirmationEmailBody(),
212217
'confirmationEmailQuestionId' => $this->getConfirmationEmailQuestionId(),
218+
'allowComments' => $this->getAllowComments(),
213219
];
214220
}
215221
}

lib/FormsMigrator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ public function import(IUser $user, IImportSource $importSource, OutputInterface
153153
$form->setConfirmationEmailSubject($formData['confirmationEmailSubject'] ?? null);
154154
$form->setConfirmationEmailBody($formData['confirmationEmailBody'] ?? null);
155155
$form->setConfirmationEmailQuestionId(null); // Set to null initially, updated after questions are imported
156+
$form->setAllowComments($formData['allowComments'] ?? false);
156157

157158
$this->formMapper->insert($form);
158159

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Forms\Listener;
10+
11+
use OCA\Forms\Db\FormMapper;
12+
use OCP\AppFramework\Db\DoesNotExistException;
13+
use OCP\Comments\CommentsEntityEvent;
14+
use OCP\EventDispatcher\Event;
15+
use OCP\EventDispatcher\IEventListener;
16+
17+
/**
18+
* @template-implements IEventListener<Event>
19+
*/
20+
class CommentsEntityListener implements IEventListener {
21+
public function __construct(
22+
protected FormMapper $formMapper,
23+
) {
24+
}
25+
26+
#[\Override]
27+
public function handle(Event $event): void {
28+
if (!$event instanceof CommentsEntityEvent) {
29+
return;
30+
}
31+
32+
// Register the 'forms' entity collection so the Comments app can
33+
// check whether a given form id allows comments.
34+
$event->addEntityCollection('forms', function ($formId) {
35+
try {
36+
$form = $this->formMapper->findById((int)$formId);
37+
} catch (DoesNotExistException $e) {
38+
return false;
39+
}
40+
return (bool)$form->getAllowComments();
41+
});
42+
}
43+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Forms\Migration;
11+
12+
use Closure;
13+
use OCP\DB\ISchemaWrapper;
14+
use OCP\DB\Types;
15+
use OCP\Migration\IOutput;
16+
use OCP\Migration\SimpleMigrationStep;
17+
use Override;
18+
19+
class Version050300Date20260511121033 extends SimpleMigrationStep {
20+
21+
/**
22+
* @param IOutput $output
23+
* @param Closure(): ISchemaWrapper $schemaClosure
24+
* @param array $options
25+
* @return null|ISchemaWrapper
26+
*/
27+
#[Override]
28+
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
29+
/** @var ISchemaWrapper $schema */
30+
$schema = $schemaClosure();
31+
$table = $schema->getTable('forms_v2_forms');
32+
$changed = false;
33+
34+
if (!$table->hasColumn('allow_comments')) {
35+
$table->addColumn('allow_comments', Types::BOOLEAN, [
36+
'notnull' => false,
37+
'default' => 0,
38+
]);
39+
$changed = true;
40+
}
41+
42+
return $changed ? $schema : null;
43+
}
44+
}

lib/ResponseDefinitions.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
* confirmationEmailSubject: ?string,
146146
* confirmationEmailBody: ?string,
147147
* confirmationEmailQuestionId: ?int,
148+
* allowComments: bool,
148149
* }
149150
*
150151
* @psalm-type FormsUploadedFile = array{

lib/Service/ConfigService.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ public function getConfirmationEmailRateLimit(): int {
6464
return $this->appConfig->getAppValueInt(Constants::CONFIG_KEY_CONFIRMATIONEMAILRATELIMIT, 3);
6565
}
6666

67+
public function getAllowComments(): bool {
68+
return $this->appConfig->getAppValueBool(Constants::CONFIG_KEY_ALLOWCOMMENTS, false);
69+
}
70+
6771
/**
6872
* Provide the full AppConfig
6973
*/
@@ -77,6 +81,7 @@ public function getAppConfig(): array {
7781
Constants::CONFIG_KEY_ALLOWCONFIRMATIONEMAIL => $this->getAllowConfirmationEmail(),
7882
Constants::CONFIG_KEY_CONFIRMATIONEMAILRATELIMIT => $this->getConfirmationEmailRateLimit(),
7983
'isMailConfigured' => $this->isMailConfigured(),
84+
Constants::CONFIG_KEY_ALLOWCOMMENTS => $this->getAllowComments(),
8085

8186
// Additional, calculated information out of Config
8287
'canCreateForms' => $this->canCreateForms()

openapi.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@
123123
"confirmationEmailEnabled",
124124
"confirmationEmailSubject",
125125
"confirmationEmailBody",
126-
"confirmationEmailQuestionId"
126+
"confirmationEmailQuestionId",
127+
"allowComments"
127128
],
128129
"properties": {
129130
"id": {
@@ -252,6 +253,9 @@
252253
"type": "integer",
253254
"format": "int64",
254255
"nullable": true
256+
},
257+
"allowComments": {
258+
"type": "boolean"
255259
}
256260
}
257261
},

0 commit comments

Comments
 (0)