Skip to content

Commit c0a8bee

Browse files
committed
Enhancement: Make tables import asynchronous
Signed-off-by: Kostiantyn Miakshyn <[email protected]>
1 parent b74bdf0 commit c0a8bee

18 files changed

+689
-87
lines changed

appinfo/info.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ Have a good time and manage whatever you want.
5959
<step>OCA\Tables\Migration\DbRowSleeveSequence</step>
6060
</post-migration>
6161
</repair-steps>
62+
<activity>
63+
<settings>
64+
<setting>OCA\Tables\Activity\Setting</setting>
65+
</settings>
66+
<filters>
67+
<filter>OCA\Tables\Activity\Filter</filter>
68+
</filters>
69+
<providers>
70+
<provider>OCA\Tables\Activity\Provider</provider>
71+
</providers>
72+
</activity>
6273
<commands>
6374
<command>OCA\Tables\Command\ListTables</command>
6475
<command>OCA\Tables\Command\AddTable</command>

appinfo/routes.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,18 @@
100100

101101
// import
102102
['name' => 'import#previewImportTable', 'url' => '/import-preview/table/{tableId}', 'verb' => 'POST'],
103-
['name' => 'import#importInTable', 'url' => '/import/table/{tableId}', 'verb' => 'POST'],
103+
['name' => 'import#scheduleImportInTable', 'url' => '/import/table/{tableId}/jobs', 'verb' => 'POST'],
104+
['name' => 'import#scheduleImportInView', 'url' => '/import/view/{viewId}/jobs', 'verb' => 'POST'],
104105
['name' => 'import#previewImportView', 'url' => '/import-preview/view/{viewId}', 'verb' => 'POST'],
105-
['name' => 'import#importInView', 'url' => '/import/view/{viewId}', 'verb' => 'POST'],
106106
['name' => 'import#previewUploadImportTable', 'url' => '/importupload-preview/table/{tableId}', 'verb' => 'POST'],
107-
['name' => 'import#importUploadInTable', 'url' => '/importupload/table/{tableId}', 'verb' => 'POST'],
107+
['name' => 'import#scheduleImportUploadInTable', 'url' => '/importupload/table/{tableId}/jobs', 'verb' => 'POST'],
108108
['name' => 'import#previewUploadImportView', 'url' => '/importupload-preview/view/{viewId}', 'verb' => 'POST'],
109+
['name' => 'import#scheduleImportUploadInView', 'url' => '/importupload/view/{viewId}/jobs', 'verb' => 'POST'],
110+
// deprecated endpoints
111+
['name' => 'import#importUploadInTable', 'url' => '/importupload/table/{tableId}', 'verb' => 'POST'],
109112
['name' => 'import#importUploadInView', 'url' => '/importupload/view/{viewId}', 'verb' => 'POST'],
113+
['name' => 'import#importInTable', 'url' => '/import/table/{tableId}', 'verb' => 'POST'],
114+
['name' => 'import#importInView', 'url' => '/import/view/{viewId}', 'verb' => 'POST'],
110115

111116
// search
112117
['name' => 'search#all', 'url' => '/search/all', 'verb' => 'GET'],

lib/Activity/ActivityConstants.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
namespace OCA\Tables\Activity;
9+
10+
class ActivityConstants {
11+
/*****
12+
* Types can have different Settings for Mail/Notifications.
13+
*/
14+
public const TYPE_IMPORT_FINISHED = 'tables_import_finished';
15+
16+
/*****
17+
* Subjects are internal 'types', that get interpreted by our own Provider.
18+
*/
19+
20+
/**
21+
* Somebody shared a form to a selected user
22+
* Needs Params:
23+
* "user": The userId of the user who shared.
24+
* "formTitle": The hash of the shared form.
25+
* "formHash": The hash of the shared form
26+
*/
27+
public const SUBJECT_IMPORT_FINISHED = 'import_finished_subject';
28+
29+
public const MESSAGE_IMPORT_FINISHED = 'import_finished_message';
30+
}

lib/Activity/ActivityManager.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
namespace OCA\Tables\Activity;
9+
10+
use OCA\Tables\AppInfo\Application;
11+
use OCA\Tables\Model\ImportStats;
12+
use OCP\Activity\IManager;
13+
14+
class ActivityManager {
15+
public function __construct(
16+
protected IManager $activityManager,
17+
) {
18+
}
19+
20+
public function notifyImportFinished(string $userId, int $tableId, ImportStats $importStats): void {
21+
22+
$activity = $this->activityManager->generateEvent();
23+
$activity->setApp(Application::APP_ID)
24+
->setType(ActivityConstants::TYPE_IMPORT_FINISHED)
25+
->setAuthor($userId)
26+
->setObject('table', $tableId)
27+
->setAffectedUser($userId)
28+
->setSubject(ActivityConstants::SUBJECT_IMPORT_FINISHED, [
29+
'actor' => $userId,
30+
'tableId' => $tableId,
31+
])
32+
->setMessage(ActivityConstants::MESSAGE_IMPORT_FINISHED, [
33+
'actor' => $userId,
34+
'tableId' => $tableId,
35+
] + (array)$importStats);
36+
37+
$this->activityManager->publish($activity);
38+
}
39+
}

lib/Activity/Filter.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
namespace OCA\Tables\Activity;
9+
10+
use OCA\Tables\AppInfo\Application;
11+
use OCP\Activity\IFilter;
12+
use OCP\IL10N;
13+
use OCP\IURLGenerator;
14+
15+
class Filter implements IFilter {
16+
public function __construct(
17+
protected IL10N $l,
18+
protected IURLGenerator $url,
19+
) {
20+
}
21+
22+
public function getIdentifier(): string {
23+
return Application::APP_ID;
24+
}
25+
26+
public function getName(): string {
27+
return $this->l->t('Tables');
28+
}
29+
30+
public function getPriority(): int {
31+
return 40;
32+
}
33+
34+
public function getIcon(): string {
35+
return $this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg'));
36+
}
37+
38+
public function filterTypes(array $types): array {
39+
return $types;
40+
}
41+
42+
public function allowedApps(): array {
43+
return [Application::APP_ID];
44+
}
45+
}

lib/Activity/Provider.php

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
namespace OCA\Tables\Activity;
8+
9+
use OCA\Tables\AppInfo\Application;
10+
use OCP\Activity\Exceptions\UnknownActivityException;
11+
use OCP\Activity\IEvent;
12+
use OCP\Activity\IManager;
13+
use OCP\Activity\IProvider;
14+
use OCP\Comments\ICommentsManager;
15+
use OCP\IL10N;
16+
use OCP\IURLGenerator;
17+
use OCP\IUserManager;
18+
use OCP\L10N\IFactory;
19+
20+
class Provider implements IProvider {
21+
protected ?IL10N $l = null;
22+
23+
public function __construct(
24+
protected IFactory $languageFactory,
25+
protected IURLGenerator $url,
26+
protected ICommentsManager $commentsManager,
27+
protected IUserManager $userManager,
28+
protected IManager $activityManager,
29+
) {
30+
}
31+
32+
/**
33+
* @param string $language
34+
* @param IEvent $event
35+
* @param IEvent|null $previousEvent
36+
* @return IEvent
37+
* @throws UnknownActivityException
38+
*/
39+
public function parse($language, IEvent $event, ?IEvent $previousEvent = null): IEvent {
40+
if ($event->getApp() !== Application::APP_ID) {
41+
throw new UnknownActivityException();
42+
}
43+
44+
$this->l = $this->languageFactory->get(Application::APP_ID, $language);
45+
46+
if ($event->getSubject() === ActivityConstants::SUBJECT_IMPORT_FINISHED) {
47+
// $event->setParsedMessage($comment->getMessage())
48+
// ->setRichMessage($message, $mentions);
49+
50+
$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg')));
51+
52+
if ($this->activityManager->isFormattingFilteredObject()) {
53+
try {
54+
return $this->parseShortVersion($event);
55+
} catch (UnknownActivityException) {
56+
// Ignore and simply use the long version...
57+
}
58+
}
59+
60+
return $this->parseLongVersion($event);
61+
}
62+
63+
throw new UnknownActivityException();
64+
}
65+
66+
/**
67+
* @throws UnknownActivityException
68+
*/
69+
protected function parseShortVersion(IEvent $event): IEvent {
70+
$subjectParameters = $this->getSubjectParameters($event);
71+
72+
if ($event->getSubject() === ActivityConstants::SUBJECT_IMPORT_FINISHED) {
73+
$event->setRichSubject($this->l->t('You commented'), []);
74+
} else {
75+
throw new UnknownActivityException();
76+
}
77+
78+
return $event;
79+
}
80+
81+
/**
82+
* @throws UnknownActivityException
83+
*/
84+
protected function parseLongVersion(IEvent $event): IEvent {
85+
$subjectParameters = $this->getSubjectParameters($event);
86+
87+
if ($event->getSubject() === ActivityConstants::SUBJECT_IMPORT_FINISHED) {
88+
$event->setParsedSubject($this->l->t('You commented on %1$s', [
89+
$subjectParameters['filePath'],
90+
]))
91+
->setRichSubject($this->l->t('You commented on {file}'), [
92+
'file' => $this->generateFileParameter($subjectParameters['fileId'], $subjectParameters['filePath']),
93+
]);
94+
} else {
95+
throw new UnknownActivityException();
96+
}
97+
98+
return $event;
99+
}
100+
101+
protected function getSubjectParameters(IEvent $event): array {
102+
$subjectParameters = $event->getSubjectParameters();
103+
if (isset($subjectParameters['fileId'])) {
104+
return $subjectParameters;
105+
}
106+
107+
return [
108+
'actor' => $subjectParameters[0],
109+
'fileId' => $event->getObjectId(),
110+
'filePath' => trim($subjectParameters[1], '/'),
111+
];
112+
}
113+
114+
/**
115+
* @return array<string, string>
116+
*/
117+
protected function generateFileParameter(int $id, string $path): array {
118+
return [
119+
'type' => 'file',
120+
'id' => (string)$id,
121+
'name' => basename($path),
122+
'path' => $path,
123+
'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $id]),
124+
];
125+
}
126+
}

lib/Activity/Setting.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
namespace OCA\Tables\Activity;
8+
9+
use OCA\Tables\AppInfo\Application;
10+
use OCP\Activity\ActivitySettings;
11+
use OCP\IL10N;
12+
13+
class Setting extends ActivitySettings {
14+
public function __construct(
15+
protected IL10N $l,
16+
) {
17+
}
18+
19+
public function getIdentifier(): string {
20+
return ActivityConstants::TYPE_IMPORT_FINISHED;
21+
}
22+
23+
public function getName(): string {
24+
return $this->l->t('<strong>Import</strong> of a file has finished');
25+
}
26+
27+
public function getGroupIdentifier() {
28+
return Application::APP_ID;
29+
}
30+
31+
public function getGroupName() {
32+
return $this->l->t('Tables');
33+
}
34+
35+
public function getPriority(): int {
36+
return 50;
37+
}
38+
39+
public function canChangeStream(): bool {
40+
return true;
41+
}
42+
43+
public function isDefaultEnabledStream(): bool {
44+
return true;
45+
}
46+
47+
public function canChangeMail(): bool {
48+
return true;
49+
}
50+
51+
public function isDefaultEnabledMail(): bool {
52+
return false;
53+
}
54+
}

0 commit comments

Comments
 (0)