From e19b79bafd8446852f8f7da4eee9bd844b8a06d9 Mon Sep 17 00:00:00 2001 From: Roberto Vidal Date: Fri, 9 May 2025 22:00:21 +0200 Subject: [PATCH 1/3] feat: setting to show/hide hidden files fixes #1245 Signed-off-by: Roberto Vidal --- lib/Controller/Helper.php | 3 ++- lib/Controller/NotesApiController.php | 4 +++- lib/Service/NotesService.php | 17 +++++++++++++---- lib/Service/SettingsService.php | 8 +++++++- src/components/AppSettings.vue | 24 +++++++++++++++++++++++- 5 files changed, 48 insertions(+), 8 deletions(-) diff --git a/lib/Controller/Helper.php b/lib/Controller/Helper.php index 065c38b9d..7f71989d1 100644 --- a/lib/Controller/Helper.php +++ b/lib/Controller/Helper.php @@ -75,11 +75,12 @@ public function getNotesAndCategories( ?string $category = null, int $chunkSize = 0, ?string $chunkCursorStr = null, + ?bool $showHidden = null, ) : array { $userId = $this->getUID(); $chunkCursor = $chunkCursorStr ? ChunkCursor::fromString($chunkCursorStr) : null; $lastUpdate = $chunkCursor->timeStart ?? new \DateTime(); - $data = $this->notesService->getAll($userId, true); // auto-create notes folder if not exists + $data = $this->notesService->getAll($userId, true, $showHidden); // auto-create notes folder if not exists $metaNotes = $this->metaService->getAll($userId, $data['notes']); // if a category is requested, then ignore all other notes diff --git a/lib/Controller/NotesApiController.php b/lib/Controller/NotesApiController.php index 8ff0a300d..6b81e3bc0 100644 --- a/lib/Controller/NotesApiController.php +++ b/lib/Controller/NotesApiController.php @@ -66,7 +66,9 @@ public function index( $this->settingsService->getAll($userId, true); // load notes and categories $exclude = explode(',', $exclude); - $data = $this->helper->getNotesAndCategories($pruneBefore, $exclude, $category, $chunkSize, $chunkCursor); + // show hidden folders by default, ignoring settings, so clients can handle them at will + $showHidden = true; + $data = $this->helper->getNotesAndCategories($pruneBefore, $exclude, $category, $chunkSize, $chunkCursor, $showHidden); $notesData = $data['notesData']; if (!$data['chunkCursor']) { // if last chunk, then send all notes (pruned) diff --git a/lib/Service/NotesService.php b/lib/Service/NotesService.php index dca19508f..e0e05f173 100644 --- a/lib/Service/NotesService.php +++ b/lib/Service/NotesService.php @@ -30,11 +30,14 @@ public function __construct( $this->noteUtil = $noteUtil; } - public function getAll(string $userId, bool $autoCreateNotesFolder = false) : array { + public function getAll(string $userId, bool $autoCreateNotesFolder = false, ?bool $showHidden = null) : array { $customExtension = $this->getCustomExtension($userId); try { $notesFolder = $this->getNotesFolder($userId, $autoCreateNotesFolder); - $data = self::gatherNoteFiles($customExtension, $notesFolder); + if ($showHidden === null) { + $showHidden = $this->settings->get($userId, 'showHidden'); + } + $data = self::gatherNoteFiles($customExtension, $notesFolder, $showHidden); $fileIds = array_keys($data['files']); // pre-load tags for all notes (performance improvement) $this->noteUtil->getTagService()->loadTags($fileIds); @@ -66,7 +69,8 @@ public function countNotes(string $userId) : int { $customExtension = $this->getCustomExtension($userId); try { $notesFolder = $this->getNotesFolder($userId, false); - $data = self::gatherNoteFiles($customExtension, $notesFolder); + $showHidden = $this->settings->get($userId, 'showHidden'); + $data = self::gatherNoteFiles($customExtension, $notesFolder, $showHidden); return count($data['files']); } catch (NotesFolderException $e) { return 0; @@ -168,6 +172,7 @@ private function getNotesFolder(string $userId, bool $create = true) : Folder { private static function gatherNoteFiles( string $customExtension, Folder $folder, + bool $showHidden, string $categoryPrefix = '', ) : array { $data = [ @@ -176,10 +181,14 @@ private static function gatherNoteFiles( ]; $nodes = $folder->getDirectoryListing(); foreach ($nodes as $node) { + $hidden = str_starts_with($node->getName(), '.'); + if ($hidden && !$showHidden) { + continue; + } if ($node->getType() === FileInfo::TYPE_FOLDER && $node instanceof Folder) { $subCategory = $categoryPrefix . $node->getName(); $data['categories'][] = $subCategory; - $data_sub = self::gatherNoteFiles($customExtension, $node, $subCategory . '/'); + $data_sub = self::gatherNoteFiles($customExtension, $node, $showHidden, $subCategory . '/'); $data['files'] = $data['files'] + $data_sub['files']; $data['categories'] = $data['categories'] + $data_sub['categories']; } elseif (self::isNote($node, $customExtension)) { diff --git a/lib/Service/SettingsService.php b/lib/Service/SettingsService.php index bda763d9f..1fc0be3ad 100644 --- a/lib/Service/SettingsService.php +++ b/lib/Service/SettingsService.php @@ -68,6 +68,12 @@ public function __construct( return '.' . $out; }, ], + 'showHidden' => [ + 'default' => true, + 'validate' => function ($value) { + return (bool)$value; + } + ], ]; } @@ -169,7 +175,7 @@ public function getAll(string $uid, $saveInitial = false) : \stdClass { /** * @throws \OCP\PreConditionNotMetException */ - public function get(string $uid, string $name) : string { + public function get(string $uid, string $name) : string|bool { $settings = $this->getAll($uid); if (property_exists($settings, $name)) { return $settings->{$name}; diff --git a/src/components/AppSettings.vue b/src/components/AppSettings.vue index 4d086dd48..9ae85cca2 100644 --- a/src/components/AppSettings.vue +++ b/src/components/AppSettings.vue @@ -22,10 +22,11 @@ {{ t('notes', 'Organize your notes in categories.') }} - +

{{ t('notes', 'Folder to store your notes') }}

+ +
+ + {{ t('notes', 'Show hidden folders') }} + +

@@ -87,6 +96,7 @@ import { NcAppSettingsDialog, NcAppSettingsSection, + NcCheckboxRadioSwitch, } from '@nextcloud/vue' import { getFilePickerBuilder } from '@nextcloud/dialogs' @@ -101,6 +111,7 @@ export default { components: { NcAppSettingsDialog, NcAppSettingsSection, + NcCheckboxRadioSwitch, HelpMobile, }, @@ -136,6 +147,7 @@ export default { { shortcut: t('notes', 'CTRL') + '+' + t('notes', 'ALT') + '+I', action: t('notes', 'Insert image') }, { shortcut: t('notes', 'CTRL') + '+/', action: t('notes', 'Switch between editor and viewer') }, ], + initialShowHidden: Boolean(store.state.app.settings.showHidden), } }, @@ -195,6 +207,12 @@ export default { setSettingsOpen(newValue) { this.settingsOpen = newValue this.$emit('update:open', newValue) + + if (this.settingsOpen) { + this.$data.initialShowHidden = Boolean(store.state.app.settings.showHidden) + } else if (this.$data.initialShowHidden !== store.state.app.settings.showHidden) { + this.$emit('reload') + } }, }, } @@ -211,4 +229,8 @@ export default { .settings-block form { display: inline-flex; } + +#notesPath { + margin-bottom: 1rem; +} From 26c8aae7fd40c832e8c8eb5ecdf3e15fb065555b Mon Sep 17 00:00:00 2001 From: Roberto Vidal Date: Wed, 14 May 2025 10:34:50 +0200 Subject: [PATCH 2/3] fix: address feedback Signed-off-by: Roberto Vidal --- lib/AppInfo/BeforeShareCreatedListener.php | 4 +-- lib/Controller/NotesController.php | 2 +- lib/Service/NoteUtil.php | 2 +- lib/Service/NotesService.php | 10 +++--- lib/Service/SettingsService.php | 38 +++++++++++++++++----- src/components/AppSettings.vue | 2 +- 6 files changed, 39 insertions(+), 19 deletions(-) diff --git a/lib/AppInfo/BeforeShareCreatedListener.php b/lib/AppInfo/BeforeShareCreatedListener.php index 454639a04..5cd9aff23 100644 --- a/lib/AppInfo/BeforeShareCreatedListener.php +++ b/lib/AppInfo/BeforeShareCreatedListener.php @@ -50,11 +50,11 @@ public function overwriteShareTarget(IShare $share): void { $itemTarget = $share->getTarget(); $uidOwner = $share->getSharedBy(); $ownerPath = $this->noteUtil->getRoot()->getUserFolder($uidOwner)->getPath(); - $ownerNotesPath = $ownerPath . '/' . $this->settings->get($uidOwner, 'notesPath'); + $ownerNotesPath = $ownerPath . '/' . $this->settings->getValueString($uidOwner, 'notesPath'); $receiver = $share->getSharedWith(); $receiverPath = $this->noteUtil->getRoot()->getUserFolder($receiver)->getPath(); - $receiverNotesInternalPath = $this->settings->get($receiver, 'notesPath'); + $receiverNotesInternalPath = $this->settings->getValueString($receiver, 'notesPath'); $this->noteUtil->getOrCreateNotesFolder($receiver); if ($itemType !== 'file' || strpos($fileSourcePath, $ownerNotesPath) !== 0) { diff --git a/lib/Controller/NotesController.php b/lib/Controller/NotesController.php index c323e8717..7a74194de 100644 --- a/lib/Controller/NotesController.php +++ b/lib/Controller/NotesController.php @@ -359,7 +359,7 @@ public function uploadFile(int $noteid): JSONResponse { } private function inLockScope(Note $note, callable $callback) { - $isRichText = $this->settingsService->get($this->helper->getUID(), 'noteMode') === 'rich'; + $isRichText = $this->settingsService->getValueString($this->helper->getUID(), 'noteMode') === 'rich'; $lockContext = new LockContext( $note->getFile(), $isRichText ? ILock::TYPE_APP : ILock::TYPE_USER, diff --git a/lib/Service/NoteUtil.php b/lib/Service/NoteUtil.php index 8329aa64b..a5dd7941f 100644 --- a/lib/Service/NoteUtil.php +++ b/lib/Service/NoteUtil.php @@ -197,7 +197,7 @@ public function getNotesFolderUserPath(string $userId): ?string { public function getOrCreateNotesFolder(string $userId, bool $create = true) : Folder { $userFolder = $this->getRoot()->getUserFolder($userId); - $notesPath = $this->settingsService->get($userId, 'notesPath'); + $notesPath = $this->settingsService->getValueString($userId, 'notesPath'); $allowShared = $notesPath !== $this->settingsService->getDefaultNotesPath($userId); $folder = null; diff --git a/lib/Service/NotesService.php b/lib/Service/NotesService.php index e0e05f173..4a7dd99e8 100644 --- a/lib/Service/NotesService.php +++ b/lib/Service/NotesService.php @@ -35,7 +35,7 @@ public function getAll(string $userId, bool $autoCreateNotesFolder = false, ?boo try { $notesFolder = $this->getNotesFolder($userId, $autoCreateNotesFolder); if ($showHidden === null) { - $showHidden = $this->settings->get($userId, 'showHidden'); + $showHidden = $this->settings->getValueBool($userId, 'showHidden'); } $data = self::gatherNoteFiles($customExtension, $notesFolder, $showHidden); $fileIds = array_keys($data['files']); @@ -69,7 +69,7 @@ public function countNotes(string $userId) : int { $customExtension = $this->getCustomExtension($userId); try { $notesFolder = $this->getNotesFolder($userId, false); - $showHidden = $this->settings->get($userId, 'showHidden'); + $showHidden = $this->settings->getValueBool($userId, 'showHidden'); $data = self::gatherNoteFiles($customExtension, $notesFolder, $showHidden); return count($data['files']); } catch (NotesFolderException $e) { @@ -132,9 +132,9 @@ public function create(string $userId, string $title, string $category) : Note { $this->noteUtil->ensureSufficientStorage($folder, 1); // get file name - $fileSuffix = $this->settings->get($userId, 'fileSuffix'); + $fileSuffix = $this->settings->getValueString($userId, 'fileSuffix'); if ($fileSuffix === 'custom') { - $fileSuffix = $this->settings->get($userId, 'customSuffix'); + $fileSuffix = $this->settings->getValueString($userId, 'customSuffix'); } $filename = $this->noteUtil->generateFileName($folder, $title, $fileSuffix, -1); // create file @@ -211,7 +211,7 @@ private static function isNote(FileInfo $file, string $customExtension) : bool { * Retrieve the value of user defined files extension */ private function getCustomExtension(string $userId) { - $suffix = $this->settings->get($userId, 'customSuffix'); + $suffix = $this->settings->getValueString($userId, 'customSuffix'); return ltrim($suffix, '.'); } diff --git a/lib/Service/SettingsService.php b/lib/Service/SettingsService.php index 1fc0be3ad..a1e3dafd2 100644 --- a/lib/Service/SettingsService.php +++ b/lib/Service/SettingsService.php @@ -69,8 +69,8 @@ public function __construct( }, ], 'showHidden' => [ - 'default' => true, - 'validate' => function ($value) { + 'default' => false, + 'validate' => function (mixed $value) : bool { return (bool)$value; } ], @@ -175,13 +175,15 @@ public function getAll(string $uid, $saveInitial = false) : \stdClass { /** * @throws \OCP\PreConditionNotMetException */ - public function get(string $uid, string $name) : string|bool { - $settings = $this->getAll($uid); - if (property_exists($settings, $name)) { - return $settings->{$name}; - } else { - throw new \OCP\PreConditionNotMetException('Setting ' . $name . ' not found for user ' . $uid . '.'); - } + public function getValueString(string $uid, string $name) : string { + return $this->get($uid, $name, 'string'); + } + + /** + * @throws \OCP\PreConditionNotMetException + */ + public function getValueBool(string $uid, string $name) : bool { + return $this->get($uid, $name, 'boolean'); } public function delete(string $uid, string $name): void { @@ -204,4 +206,22 @@ private function getAvailableEditorModes(): array { ? ['rich', 'edit', 'preview'] : ['edit', 'preview']; } + + /** + * @throws \OCP\PreConditionNotMetException + */ + private function get(string $uid, string $name, string $type) : mixed { + $settings = $this->getAll($uid); + if (property_exists($settings, $name)) { + $value = $settings->{$name}; + if (gettype($value) !== $type) { + throw new \TypeError('Invalid type'); + } + + return $value; + } else { + throw new \OCP\PreConditionNotMetException('Setting ' . $name . ' not found for user ' . $uid . '.'); + } + } + } diff --git a/src/components/AppSettings.vue b/src/components/AppSettings.vue index 9ae85cca2..fe61bd347 100644 --- a/src/components/AppSettings.vue +++ b/src/components/AppSettings.vue @@ -22,7 +22,7 @@ {{ t('notes', 'Organize your notes in categories.') }} - +

{{ t('notes', 'Folder to store your notes') }}

From 27ddb2cf19ad9870d886c79abe7b3380e5afbfe2 Mon Sep 17 00:00:00 2001 From: Roberto Vidal Date: Wed, 14 May 2025 14:59:43 +0200 Subject: [PATCH 3/3] fix: respect setting for api endpoints Signed-off-by: Roberto Vidal --- lib/Controller/Helper.php | 3 +-- lib/Controller/NotesApiController.php | 4 +--- lib/Service/NotesService.php | 6 ++---- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/Controller/Helper.php b/lib/Controller/Helper.php index 7f71989d1..065c38b9d 100644 --- a/lib/Controller/Helper.php +++ b/lib/Controller/Helper.php @@ -75,12 +75,11 @@ public function getNotesAndCategories( ?string $category = null, int $chunkSize = 0, ?string $chunkCursorStr = null, - ?bool $showHidden = null, ) : array { $userId = $this->getUID(); $chunkCursor = $chunkCursorStr ? ChunkCursor::fromString($chunkCursorStr) : null; $lastUpdate = $chunkCursor->timeStart ?? new \DateTime(); - $data = $this->notesService->getAll($userId, true, $showHidden); // auto-create notes folder if not exists + $data = $this->notesService->getAll($userId, true); // auto-create notes folder if not exists $metaNotes = $this->metaService->getAll($userId, $data['notes']); // if a category is requested, then ignore all other notes diff --git a/lib/Controller/NotesApiController.php b/lib/Controller/NotesApiController.php index 6b81e3bc0..8ff0a300d 100644 --- a/lib/Controller/NotesApiController.php +++ b/lib/Controller/NotesApiController.php @@ -66,9 +66,7 @@ public function index( $this->settingsService->getAll($userId, true); // load notes and categories $exclude = explode(',', $exclude); - // show hidden folders by default, ignoring settings, so clients can handle them at will - $showHidden = true; - $data = $this->helper->getNotesAndCategories($pruneBefore, $exclude, $category, $chunkSize, $chunkCursor, $showHidden); + $data = $this->helper->getNotesAndCategories($pruneBefore, $exclude, $category, $chunkSize, $chunkCursor); $notesData = $data['notesData']; if (!$data['chunkCursor']) { // if last chunk, then send all notes (pruned) diff --git a/lib/Service/NotesService.php b/lib/Service/NotesService.php index 4a7dd99e8..1e60cf561 100644 --- a/lib/Service/NotesService.php +++ b/lib/Service/NotesService.php @@ -30,13 +30,11 @@ public function __construct( $this->noteUtil = $noteUtil; } - public function getAll(string $userId, bool $autoCreateNotesFolder = false, ?bool $showHidden = null) : array { + public function getAll(string $userId, bool $autoCreateNotesFolder = false) : array { $customExtension = $this->getCustomExtension($userId); try { $notesFolder = $this->getNotesFolder($userId, $autoCreateNotesFolder); - if ($showHidden === null) { - $showHidden = $this->settings->getValueBool($userId, 'showHidden'); - } + $showHidden = $this->settings->getValueBool($userId, 'showHidden'); $data = self::gatherNoteFiles($customExtension, $notesFolder, $showHidden); $fileIds = array_keys($data['files']); // pre-load tags for all notes (performance improvement)