Skip to content

Commit 3181423

Browse files
authored
Merge pull request #1479 from nextcloud/feat/sort-recent-forms
Add lastUpdated property to Form
2 parents b3b24b8 + d179439 commit 3181423

File tree

20 files changed

+244
-17
lines changed

20 files changed

+244
-17
lines changed

lib/Controller/ApiController.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ public function newForm(): DataResponse {
269269
$form->setShowExpiration(false);
270270
$form->setExpires(0);
271271
$form->setIsAnonymous(false);
272+
$form->setLastUpdated(time());
272273

273274
$this->formMapper->insert($form);
274275

@@ -315,6 +316,7 @@ public function cloneForm(int $id): DataResponse {
315316
$formData = $oldForm->read();
316317
unset($formData['id']);
317318
$formData['created'] = time();
319+
$formData['lastUpdated'] = time();
318320
$formData['hash'] = $this->formsService->generateFormHash();
319321
// TRANSLATORS Appendix to the form Title of a duplicated/copied form.
320322
$formData['title'] .= ' - ' . $this->l10n->t('Copy');
@@ -384,9 +386,10 @@ public function updateForm(int $id, array $keyValuePairs): DataResponse {
384386
throw new OCSForbiddenException();
385387
}
386388

387-
// Don't allow to change params id, hash, ownerId, created
389+
// Don't allow to change params id, hash, ownerId, created, lastUpdated
388390
if (key_exists('id', $keyValuePairs) || key_exists('hash', $keyValuePairs) ||
389-
key_exists('ownerId', $keyValuePairs) || key_exists('created', $keyValuePairs)) {
391+
key_exists('ownerId', $keyValuePairs) || key_exists('created', $keyValuePairs) ||
392+
key_exists('lastUpdated', $keyValuePairs)) {
390393
$this->logger->info('Not allowed to update id, hash, ownerId or created');
391394
throw new OCSForbiddenException();
392395
}
@@ -397,6 +400,7 @@ public function updateForm(int $id, array $keyValuePairs): DataResponse {
397400

398401
// Update changed Columns in Db.
399402
$this->formMapper->update($form);
403+
$this->formsService->setLastUpdatedTimestamp($id);
400404

401405
return new DataResponse($form->getId());
402406
}
@@ -501,6 +505,8 @@ public function newQuestion(int $formId, string $type, string $text = ''): DataR
501505
$response = $question->read();
502506
$response['options'] = [];
503507

508+
$this->formsService->setLastUpdatedTimestamp($formId);
509+
504510
return new DataResponse($response);
505511
}
506512

@@ -594,6 +600,8 @@ public function reorderQuestions(int $formId, array $newOrder): DataResponse {
594600
];
595601
}
596602

603+
$this->formsService->setLastUpdatedTimestamp($formId);
604+
597605
return new DataResponse($response);
598606
}
599607

@@ -654,6 +662,8 @@ public function updateQuestion(int $id, array $keyValuePairs): DataResponse {
654662
// Update changed Columns in Db.
655663
$this->questionMapper->update($question);
656664

665+
$this->formsService->setLastUpdatedTimestamp($form->getId());
666+
657667
return new DataResponse($question->getId());
658668
}
659669

@@ -703,6 +713,8 @@ public function deleteQuestion(int $id): DataResponse {
703713
}
704714
}
705715

716+
$this->formsService->setLastUpdatedTimestamp($form->getId());
717+
706718
return new DataResponse($id);
707719
}
708720

@@ -744,6 +756,8 @@ public function newOption(int $questionId, string $text): DataResponse {
744756

745757
$option = $this->optionMapper->insert($option);
746758

759+
$this->formsService->setLastUpdatedTimestamp($form->getId());
760+
747761
return new DataResponse($option->read());
748762
}
749763

@@ -798,6 +812,8 @@ public function updateOption(int $id, array $keyValuePairs): DataResponse {
798812
// Update changed Columns in Db.
799813
$this->optionMapper->update($option);
800814

815+
$this->formsService->setLastUpdatedTimestamp($form->getId());
816+
801817
return new DataResponse($option->getId());
802818
}
803819

@@ -833,6 +849,8 @@ public function deleteOption(int $id): DataResponse {
833849

834850
$this->optionMapper->delete($option);
835851

852+
$this->formsService->setLastUpdatedTimestamp($form->getId());
853+
836854
return new DataResponse($id);
837855
}
838856

@@ -1013,6 +1031,8 @@ public function insertSubmission(int $formId, array $answers, string $shareHash
10131031
}
10141032
}
10151033

1034+
$this->formsService->setLastUpdatedTimestamp($formId);
1035+
10161036
//Create Activity
10171037
$this->activityManager->publishNewSubmission($form, $submission->getUserId());
10181038

@@ -1051,6 +1071,8 @@ public function deleteSubmission(int $id): DataResponse {
10511071
// Delete submission (incl. Answers)
10521072
$this->submissionMapper->deleteById($id);
10531073

1074+
$this->formsService->setLastUpdatedTimestamp($form->getId());
1075+
10541076
return new DataResponse($id);
10551077
}
10561078

@@ -1085,6 +1107,8 @@ public function deleteAllSubmissions(int $formId): DataResponse {
10851107
// Delete all submissions (incl. Answers)
10861108
$this->submissionMapper->deleteByForm($formId);
10871109

1110+
$this->formsService->setLastUpdatedTimestamp($formId);
1111+
10881112
return new DataResponse($formId);
10891113
}
10901114

lib/Controller/ShareApiController.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ public function newShare(int $formId, int $shareType, string $shareWith = '', ar
211211

212212
// Create share-notifications (activity)
213213
$this->formsService->notifyNewShares($form, $share);
214+
215+
$this->formsService->setLastUpdatedTimestamp($formId);
214216

215217
// Append displayName for Frontend
216218
$shareData = $share->read();
@@ -250,6 +252,8 @@ public function deleteShare(int $id): DataResponse {
250252

251253
$this->shareMapper->deleteById($id);
252254

255+
$this->formsService->setLastUpdatedTimestamp($form->getId());
256+
253257
return new DataResponse($id);
254258
}
255259

@@ -303,6 +307,8 @@ public function updateShare(int $id, array $keyValuePairs): DataResponse {
303307
$share->setPermissions($keyValuePairs['permissions']);
304308
$share = $this->shareMapper->update($share);
305309

310+
$this->formsService->setLastUpdatedTimestamp($form->getId());
311+
306312
return new DataResponse($share->getId());
307313
}
308314

lib/Db/Form.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
* @method void setSubmitMultiple(bool $value)
5151
* @method integer getShowExpiration()
5252
* @method void setShowExpiration(bool $value)
53+
* @method integer getLastUpdated()
54+
* @method void setLastUpdated(integer $value)
5355
*/
5456
class Form extends Entity {
5557
protected $hash;
@@ -62,6 +64,7 @@ class Form extends Entity {
6264
protected $isAnonymous;
6365
protected $submitMultiple;
6466
protected $showExpiration;
67+
protected $lastUpdated;
6568

6669
/**
6770
* Form constructor.
@@ -72,6 +75,7 @@ public function __construct() {
7275
$this->addType('isAnonymous', 'bool');
7376
$this->addType('submitMultiple', 'bool');
7477
$this->addType('showExpiration', 'bool');
78+
$this->addType('lastUpdated', 'integer');
7579
}
7680

7781
// JSON-Decoding of access-column.
@@ -97,7 +101,8 @@ public function read() {
97101
'expires' => (int)$this->getExpires(),
98102
'isAnonymous' => (bool)$this->getIsAnonymous(),
99103
'submitMultiple' => (bool)$this->getSubmitMultiple(),
100-
'showExpiration' => (bool)$this->getShowExpiration()
104+
'showExpiration' => (bool)$this->getShowExpiration(),
105+
'lastUpdated' => (int)$this->getLastUpdated()
101106
];
102107
}
103108
}

lib/Db/FormMapper.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,9 @@ public function findAll(): array {
9999

100100
$qb->select('*')
101101
->from($this->getTableName())
102-
//Newest forms first
103-
->orderBy('created', 'DESC');
102+
//Last updated forms first, then newest forms first
103+
->addOrderBy('last_updated', 'DESC')
104+
->addOrderBy('created', 'DESC');
104105

105106
return $this->findEntities($qb);
106107
}
@@ -116,8 +117,9 @@ public function findAllByOwnerId(string $ownerId): array {
116117
->where(
117118
$qb->expr()->eq('owner_id', $qb->createNamedParameter($ownerId))
118119
)
119-
//Newest forms first
120-
->orderBy('created', 'DESC');
120+
//Last updated forms first, then newest forms first
121+
->addOrderBy('last_updated', 'DESC')
122+
->addOrderBy('created', 'DESC');
121123

122124
return $this->findEntities($qb);
123125
}

lib/FormsMigrator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ public function import(IUser $user, IImportSource $importSource, OutputInterface
199199
$form->setIsAnonymous($formData['isAnonymous']);
200200
$form->setSubmitMultiple($formData['submitMultiple']);
201201
$form->setShowExpiration($formData['showExpiration']);
202+
$form->setLastUpdated($formData['lastUpdated']);
202203

203204
$this->formMapper->insert($form);
204205

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2023 Christian Hartmann <[email protected]>
7+
*
8+
* @author Christian Hartmann <[email protected]>
9+
*
10+
* @license GNU AGPL version 3 or any later version
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License as
14+
* published by the Free Software Foundation, either version 3 of the
15+
* License, or (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU Affero General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*
25+
*/
26+
27+
namespace OCA\Forms\Migration;
28+
29+
use Closure;
30+
use OCP\DB\ISchemaWrapper;
31+
use OCP\DB\Types;
32+
use OCP\Migration\IOutput;
33+
use OCP\Migration\SimpleMigrationStep;
34+
35+
class Version030100Date20230202175747 extends SimpleMigrationStep {
36+
37+
/**
38+
* @param IOutput $output
39+
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
40+
* @param array $options
41+
* @return null|ISchemaWrapper
42+
*/
43+
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
44+
/** @var ISchemaWrapper $schema */
45+
$schema = $schemaClosure();
46+
$table = $schema->getTable('forms_v2_forms');
47+
48+
if (!$table->hasColumn('last_updated')) {
49+
$table->addColumn('last_updated', Types::INTEGER, [
50+
'notnull' => false,
51+
'default' => 0,
52+
'comment' => 'unix-timestamp',
53+
]);
54+
55+
return $schema;
56+
}
57+
58+
return null;
59+
}
60+
}

lib/Service/FormsService.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ public function getPartialFormArray(int $id): array {
226226
'hash' => $form->getHash(),
227227
'title' => $form->getTitle(),
228228
'expires' => $form->getExpires(),
229+
'lastUpdated' => $form->getLastUpdated(),
229230
'permissions' => $this->getPermissions($form->getId()),
230231
'partial' => true
231232
];
@@ -527,4 +528,15 @@ protected function getSharesWithUser(int $formId, string $userId): array {
527528
}
528529
});
529530
}
531+
532+
/**
533+
* Update lastUpdated timestamp for the given form
534+
*
535+
* @param int $formId The form to update
536+
*/
537+
public function setLastUpdatedTimestamp(int $formId): void {
538+
$form = $this->formMapper->findById($formId);
539+
$form->setLastUpdated(time());
540+
$this->formMapper->update($form);
541+
}
530542
}

src/Forms.vue

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,12 @@
103103
</template>
104104

105105
<script>
106-
import { emit } from '@nextcloud/event-bus'
106+
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
107107
import { generateOcsUrl } from '@nextcloud/router'
108108
import { loadState } from '@nextcloud/initial-state'
109109
import { showError } from '@nextcloud/dialogs'
110110
import axios from '@nextcloud/axios'
111+
import moment from '@nextcloud/moment'
111112
112113
import NcAppContent from '@nextcloud/vue/dist/Components/NcAppContent.js'
113114
import NcAppNavigation from '@nextcloud/vue/dist/Components/NcAppNavigation.js'
@@ -224,6 +225,14 @@ export default {
224225
this.loadForms()
225226
},
226227
228+
mounted() {
229+
subscribe('forms:last-updated:set', (id) => this.onLastUpdatedByEventBus(id))
230+
},
231+
232+
unmounted() {
233+
unsubscribe('forms:last-updated:set', (id) => this.onLastUpdatedByEventBus(id))
234+
},
235+
227236
methods: {
228237
/**
229238
* Closes the App-Navigation on mobile-devices
@@ -349,6 +358,23 @@ export default {
349358
this.$router.push({ name: 'root' })
350359
}
351360
},
361+
362+
/**
363+
* Update last updated timestamp in given form
364+
*
365+
* @param {number} id the form id
366+
*/
367+
onLastUpdatedByEventBus(id) {
368+
const formIndex = this.forms.findIndex(form => form.id === id)
369+
if (formIndex !== -1) {
370+
this.forms[formIndex].lastUpdated = moment().unix()
371+
this.forms.sort((b, a) => a.lastUpdated - b.lastUpdated)
372+
} else {
373+
const sharedFormIndex = this.sharedForms.findIndex(form => form.id === id)
374+
this.sharedForms[sharedFormIndex].lastUpdated = moment().unix()
375+
this.sharedForms.sort((a, b) => a.lastUpdated - b.lastUpdated)
376+
}
377+
},
352378
},
353379
}
354380
</script>

src/components/Questions/QuestionDropdown.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,9 @@
8282
</template>
8383

8484
<script>
85-
import { generateOcsUrl } from '@nextcloud/router'
8685
import { showError } from '@nextcloud/dialogs'
86+
import { emit } from '@nextcloud/event-bus'
87+
import { generateOcsUrl } from '@nextcloud/router'
8788
import axios from '@nextcloud/axios'
8889
import NcActionCheckbox from '@nextcloud/vue/dist/Components/NcActionCheckbox.js'
8990
import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect.js'
@@ -190,6 +191,7 @@ export default {
190191
*/
191192
updateOptions(options) {
192193
this.$emit('update:options', options)
194+
emit('forms:last-updated:set', this.$attrs.formId)
193195
},
194196
195197
/**

src/components/Questions/QuestionMultiple.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@
9090
</template>
9191

9292
<script>
93-
import { generateOcsUrl } from '@nextcloud/router'
9493
import { showError } from '@nextcloud/dialogs'
94+
import { emit } from '@nextcloud/event-bus'
95+
import { generateOcsUrl } from '@nextcloud/router'
9596
import axios from '@nextcloud/axios'
9697
import NcActionCheckbox from '@nextcloud/vue/dist/Components/NcActionCheckbox.js'
9798
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
@@ -219,6 +220,7 @@ export default {
219220
*/
220221
updateOptions(options) {
221222
this.$emit('update:options', options)
223+
emit('forms:last-updated:set', this.$attrs.formId)
222224
},
223225
224226
/**

0 commit comments

Comments
 (0)