Passed
Push — master ( b762ba...10d0d6 )
by korelstar
02:44
created

NotesService::delete()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 6
rs 10
cc 1
nc 1
nop 2
1
<?php
2
3
namespace OCA\Notes\Service;
4
5
use OCP\Encryption\Exceptions\GenericEncryptionException;
6
use OCP\Files\File;
7
use OCP\Files\FileInfo;
8
use OCP\Files\Folder;
9
use OCP\Files\IRootFolder;
10
use OCP\IConfig;
11
use OCP\IL10N;
12
use OCP\ILogger;
13
use OCP\ITagManager;
14
15
use OCA\Notes\Db\Note;
16
use OCA\Notes\Service\SettingsService;
17
18
/**
19
 * Class NotesService
20
 *
21
 * @package OCA\Notes\Service
22
 */
23
class NotesService {
24
25
	private $l10n;
26
	private $root;
27
	private $logger;
28
	private $config;
29
	private $tags;
30
	private $settings;
31
	private $noteUtil;
32
	private $appName;
33
34
	/**
35
	 * @param IRootFolder $root
36
	 * @param IL10N $l10n
37
	 * @param ILogger $logger
38
	 * @param IConfig $config
39
	 * @param ITagManager $tagManager
40
	 * @param SettingsService $settings
41
	 * @param NoteUtil $noteUtil
42
	 * @param String $appName
43
	 */
44
	public function __construct(
45
		IRootFolder $root,
46
		IL10N $l10n,
47
		ILogger $logger,
48
		IConfig $config,
49
		ITagManager $tagManager,
50
		SettingsService $settings,
51
		NoteUtil $noteUtil,
52
		$appName
53
	) {
54
		$this->root = $root;
55
		$this->l10n = $l10n;
56
		$this->logger = $logger;
57
		$this->config = $config;
58
		$this->tags = $tagManager->load('files');
59
		$this->settings = $settings;
60
		$this->noteUtil = $noteUtil;
61
		$this->appName = $appName;
62
	}
63
64
65
	/**
66
	 * @param string $userId
67
	 * @return array with all notes in the current directory
68
	 */
69
	public function getAll($userId, $onlyMeta = false) {
70
		$notesFolder = $this->getFolderForUser($userId);
71
		$notes = $this->noteUtil->gatherNoteFiles($notesFolder);
72
		$filesById = [];
73
		foreach ($notes as $note) {
74
			$filesById[$note->getId()] = $note;
75
		}
76
		$tags = $this->tags->getTagsForObjects(array_keys($filesById));
77
78
		$notes = [];
79
		foreach ($filesById as $id => $file) {
80
			$noteTags = is_array($tags) && array_key_exists($id, $tags) ? $tags[$id] : [];
81
			$notes[] = $this->getNote($file, $notesFolder, $noteTags, $onlyMeta);
82
		}
83
84
		return $notes;
85
	}
86
87
88
	/**
89
	 * Used to get a single note by id
90
	 * @param int $id the id of the note to get
91
	 * @param string $userId
92
	 * @throws NoteDoesNotExistException if note does not exist
93
	 * @return Note
94
	 */
95
	public function get($id, $userId, $onlyMeta = false) : Note {
96
		$folder = $this->getFolderForUser($userId);
97
		return $this->getNote($this->getFileById($folder, $id), $folder, $this->getTags($id), $onlyMeta);
98
	}
99
100
	private function getTags($id) {
101
		$tags = $this->tags->getTagsForObjects([$id]);
102
		return is_array($tags) && array_key_exists($id, $tags) ? $tags[$id] : [];
103
	}
104
105
	private function getNote(File $file, Folder $notesFolder, $tags = [], $onlyMeta = false) : Note {
106
		$id = $file->getId();
107
		try {
108
			$note = Note::fromFile($file, $notesFolder, $tags, $onlyMeta);
109
		} catch (GenericEncryptionException $e) {
110
			$message = $this->l10n->t('Encryption Error').': ('.$file->getName().') '.$e->getMessage();
111
			$note = Note::fromException($message, $file, $notesFolder, array_key_exists($id, $tags) ? $tags[$id] : []);
112
		} catch (\Exception $e) {
113
			$message = $this->l10n->t('Error').': ('.$file->getName().') '.$e->getMessage();
114
			$note = Note::fromException($message, $file, $notesFolder, array_key_exists($id, $tags) ? $tags[$id] : []);
115
		}
116
		return $note;
117
	}
118
119
120
	/**
121
	 * Creates a note and returns the empty note
122
	 * @param string $userId
123
	 * @see update for setting note content
124
	 * @return Note the newly created note
125
	 */
126
	public function create($userId) : Note {
127
		$title = $this->l10n->t('New note');
128
		$folder = $this->getFolderForUser($userId);
129
		if ($folder->getFreeSpace() <= 1) {
130
			throw new InsufficientStorageException();
131
		}
132
133
		// check new note exists already and we need to number it
134
		// pass -1 because no file has id -1 and that will ensure
135
		// to only return filenames that dont yet exist
136
		$path = $this->noteUtil->generateFileName($folder, $title, $this->settings->get($userId, 'fileSuffix'), -1);
137
		$file = $folder->newFile($path);
138
139
		// try to write some content
140
		try {
141
			// If server-side encryption is activated, the server creates an empty file without signature
142
			// which leads to an GenericEncryptionException('Missing Signature') afterwards.
143
			// Saving a space-char (and removing it later) is a working work-around.
144
			$file->putContent(' ');
145
		} catch (\Throwable $e) {
146
			// if writing the content fails, we have to roll back the note creation
147
			$this->delete($file->getId(), $userId);
148
			throw $e;
149
		}
150
151
		return $this->getNote($file, $folder);
152
	}
153
154
155
	/**
156
	 * Updates a note. Be sure to check the returned note since the title is
157
	 * dynamically generated and filename conflicts are resolved
158
	 * @param int $id the id of the note used to update
159
	 * @param string|null $content the content which will be written into the note
160
	 * the title is generated from the first line of the content
161
	 * @param string|null $category the category in which the note should be saved
162
	 * @param int $mtime time of the note modification (optional)
163
	 * @throws NoteDoesNotExistException if note does not exist
164
	 * @return \OCA\Notes\Db\Note the updated note
165
	 */
166
	public function update($id, $content, $userId, $category = null, $mtime = 0) : Note {
167
		$notesFolder = $this->getFolderForUser($userId);
168
		$file = $this->getFileById($notesFolder, $id);
169
		$title = $this->noteUtil->getSafeTitleFromContent($content===null ? $file->getContent() : $content);
170
171
		// rename/move file with respect to title/category
172
		// this can fail if access rights are not sufficient or category name is illegal
173
		try {
174
			$this->noteUtil->moveNote($notesFolder, $file, $category, $title);
175
		} catch (\OCP\Files\NotPermittedException $e) {
176
			$err = 'Moving note '.$id.' ('.$title.') to the desired target is not allowed.'
177
				.' Please check the note\'s target category ('.$category.').';
178
			$this->logger->error($err, ['app' => $this->appName]);
179
		} catch (\Exception $e) {
180
			$err = 'Moving note '.$id.' ('.$title.') to the desired target has failed '
181
				.'with a '.get_class($e).': '.$e->getMessage();
182
			$this->logger->error($err, ['app' => $this->appName]);
183
		}
184
185
		if ($content !== null) {
186
			if ($file->getParent()->getFreeSpace() < strlen($content)) {
187
				throw new InsufficientStorageException();
188
			}
189
			$file->putContent($content);
190
		}
191
192
		if ($mtime) {
193
			$file->touch($mtime);
194
		}
195
196
		return $this->getNote($file, $notesFolder, $this->getTags($id));
197
	}
198
199
	/**
200
	 * Set or unset a note as favorite.
201
	 * @param int $id the id of the note used to update
202
	 * @param boolean $favorite whether the note should be a favorite or not
203
	 * @throws NoteDoesNotExistException if note does not exist
204
	 * @return boolean the new favorite state of the note
205
	 */
206
	public function favorite($id, $favorite, $userId) {
207
		$note = $this->get($id, $userId, true);
208
		if ($favorite !== $note->getFavorite()) {
209
			if ($favorite) {
210
				$this->tags->addToFavorites($id);
211
			} else {
212
				$this->tags->removeFromFavorites($id);
213
			}
214
			$note = $this->get($id, $userId, true);
215
		}
216
		return $note->getFavorite();
217
	}
218
219
220
	/**
221
	 * Deletes a note
222
	 * @param int $id the id of the note which should be deleted
223
	 * @param string $userId
224
	 * @throws NoteDoesNotExistException if note does not
225
	 * exist
226
	 */
227
	public function delete($id, $userId) {
228
		$notesFolder = $this->getFolderForUser($userId);
229
		$file = $this->getFileById($notesFolder, $id);
230
		$parent = $file->getParent();
231
		$file->delete();
232
		$this->noteUtil->deleteEmptyFolder($notesFolder, $parent);
233
	}
234
235
	/**
236
	 * @param Folder $folder
237
	 * @param int $id
238
	 * @throws NoteDoesNotExistException
239
	 * @return \OCP\Files\File
240
	 */
241
	private function getFileById(Folder $folder, $id) : File {
242
		$file = $folder->getById($id);
243
244
		if (count($file) <= 0 || !($file[0] instanceof File) || !$this->noteUtil->isNote($file[0])) {
245
			throw new NoteDoesNotExistException();
246
		}
247
		return $file[0];
248
	}
249
250
	/**
251
	 * @param string $userId the user id
252
	 * @return boolean true if folder is accessible, or Exception otherwise
253
	 */
254
	public function checkNotesFolder($userId) {
255
		$this->getFolderForUser($userId);
256
		return true;
257
	}
258
259
	/**
260
	 * @param string $userId the user id
261
	 * @return Folder
262
	 */
263
	private function getFolderForUser($userId) : Folder {
264
		// TODO use IRootFolder->getUserFolder()  ?
265
		$path = '/' . $userId . '/files/' . $this->settings->get($userId, 'notesPath');
266
		try {
267
			$folder = $this->noteUtil->getOrCreateFolder($path);
268
		} catch (\Exception $e) {
269
			throw new NotesFolderException($path);
270
		}
271
		return $folder;
272
	}
273
}
274