NotesService::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
1
<?php
2
/**
3
 * ownCloud - Notes
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Bernhard Posselt <[email protected]>
9
 * @copyright Bernhard Posselt 2012, 2014
10
 */
11
12
namespace OCA\Notes\Service;
13
14
use OCP\IL10N;
15
use OCP\Files\IRootFolder;
16
use OCP\Files\Folder;
17
18
use OCA\Notes\Db\Note;
19
20
/**
21
 * Class NotesService
22
 *
23
 * @package OCA\Notes\Service
24
 */
25
class NotesService {
26
	private $l10n;
27
	private $root;
28
29
	/**
30
	 * @param IRootFolder $root
31
	 * @param IL10N $l10n
32
	 */
33
	public function __construct(IRootFolder $root, IL10N $l10n) {
34 11
		$this->root = $root;
35 11
		$this->l10n = $l10n;
36 11
	}
37 11
38
	/**
39
	 * @param string $userId
40
	 * @return array with all notes in the current directory
41
	 */
42
	public function getAll($userId) {
43
		$folder = $this->getFolderForUser($userId);
44 1
		$files = $folder->getDirectoryListing();
45 1
		$filesById = [];
46 1
		foreach ($files as $file) {
47 1
			if ($this->isNote($file)) {
48 1
				$filesById[$file->getId()] = $file;
49 1
			}
50 1
		}
51 1
		$tagger = \OC::$server->getTagManager()->load('files');
52 1
		if ($tagger==null) {
53 1
			$tags = [];
54 1
		} else {
55 1
			$tags = $tagger->getTagsForObjects(\array_keys($filesById));
56 1
		}
57
58
		$notes = [];
59
		foreach ($filesById as $id=>$file) {
60 1
			$notes[] = Note::fromFile($file, \array_key_exists($id, $tags) ? $tags[$id] : []);
61 1
		}
62 1
63 1
		return $notes;
64
	}
65 1
66
	/**
67
	 * Used to get a single note by id
68
	 * @param int $id the id of the note to get
69
	 * @param string $userId
70
	 * @throws NoteDoesNotExistException if note does not exist
71
	 * @return Note
72
	 */
73
	public function get($id, $userId) {
74
		$folder = $this->getFolderForUser($userId);
75
		return Note::fromFile($this->getFileById($folder, $id), $this->getTags($id));
76 3
	}
77 3
78 3
	private function getTags($id) {
79
		$tagger = \OC::$server->getTagManager()->load('files');
80
		if ($tagger==null) {
81 3
			$tags = [];
82 3
		} else {
83 3
			$tags = $tagger->getTagsForObjects([$id]);
84 3
		}
85 3
		return \array_key_exists($id, $tags) ? $tags[$id] : [];
86
	}
87
88 3
	/**
89
	 * Creates a note and returns the empty note
90
	 * @param string $userId
91
	 * @see update for setting note content
92
	 * @return Note the newly created note
93
	 */
94
	public function create($userId) {
95
		$title = $this->l10n->t('New note');
96
		$folder = $this->getFolderForUser($userId);
97 2
98 2
		// check new note exists already and we need to number it
99 2
		// pass -1 because no file has id -1 and that will ensure
100
		// to only return filenames that dont yet exist
101
		$path = $this->generateFileName($folder, $title, "txt", -1);
102
		$file = $folder->newFile($path);
103
104 2
		return Note::fromFile($file);
105 2
	}
106
107 2
	/**
108
	 * Updates a note. Be sure to check the returned note since the title is
109
	 * dynamically generated and filename conflicts are resolved
110
	 * @param int $id the id of the note used to update
111
	 * @param string $content the content which will be written into the note
112
	 * the title is generated from the first line of the content
113
	 * @throws NoteDoesNotExistException if note does not exist
114
	 * @return \OCA\Notes\Db\Note the updated note
115
	 */
116
	public function update($id, $content, $userId) {
117
		$folder = $this->getFolderForUser($userId);
118
		$file = $this->getFileById($folder, $id);
119
120 2
		// generate content from the first line of the title
121 2
		$splitContent = \explode("\n", $content);
122 2
		$title = $splitContent[0];
123
124
		if (!$title) {
125 2
			$title = $this->l10n->t('New note');
126 2
		}
127
128 2
		// prevent directory traversal
129 1
		$title = \str_replace(['/', '\\'], '', $title);
130 1
		// remove hash and space characters from the beginning of the filename
131
		// in case of markdown
132
		$title = \ltrim($title, ' #');
133 2
		// using a maximum of 100 chars should be enough
134
		$title = \mb_substr($title, 0, 100, "UTF-8");
135
136 2
		// generate filename if there were collisions
137
		$currentFilePath = $file->getPath();
138 2
		$basePath = '/' . $userId . '/files/Notes/';
139
		$fileExtension = \pathinfo($file->getName(), PATHINFO_EXTENSION);
140
		$newFilePath = $basePath . $this->generateFileName($folder, $title, $fileExtension, $id);
141 2
142 2
		// if the current path is not the new path, the file has to be renamed
143 2
		if ($currentFilePath !== $newFilePath) {
144 2
			$file->move($newFilePath);
145
		}
146
147 2
		$file->putContent($content === null ? '' : $content);
148 2
149 2
		return Note::fromFile($file, $this->getTags($id));
150
	}
151 2
152
	/**
153 2
	 * Set or unset a note as favorite.
154
	 * @param int $id the id of the note used to update
155
	 * @param boolean $favorite whether the note should be a favorite or not
156
	 * @throws NoteDoesNotExistException if note does not exist
157
	 * @return boolean the new favorite state of the note
158
	 */
159
	public function favorite($id, $favorite, $userId) {
160
		$folder = $this->getFolderForUser($userId);
161
		$file = $this->getFileById($folder, $id);
162
		if (!$this->isNote($file)) {
163
			throw new NoteDoesNotExistException();
164
		}
165
		$tagger = \OC::$server->getTagManager()->load('files');
166
		if ($favorite) {
167
			$tagger->addToFavorites($id);
168
		} else {
169
			$tagger->removeFromFavorites($id);
170
		}
171
172
		$tags = $tagger->getTagsForObjects([$id]);
173
		return \in_array(\OC\Tags::TAG_FAVORITE, $tags[$id]);
174
	}
175
176
	/**
177
	 * Deletes a note
178
	 * @param int $id the id of the note which should be deleted
179
	 * @param string $userId
180
	 * @throws NoteDoesNotExistException if note does not
181
	 * exist
182
	 */
183
	public function delete($id, $userId) {
184
		$folder = $this->getFolderForUser($userId);
185
		$file = $this->getFileById($folder, $id);
186
		$file->delete();
187
	}
188 3
189 3
	/**
190 3
	 * @param Folder $folder
191 1
	 * @param int $id
192 1
	 * @throws NoteDoesNotExistException
193
	 * @return \OCP\Files\File
194
	 */
195
	private function getFileById($folder, $id) {
196
		$file = $folder->getById($id);
197
198
		if (\count($file) <= 0 || !$this->isNote($file[0])) {
199
			throw new NoteDoesNotExistException();
200
		}
201 8
		return $file[0];
202 8
	}
203
204 8
	/**
205 4
	 * @param string $userId the user id
206
	 * @return Folder
207 4
	 */
208
	private function getFolderForUser($userId) {
209
		$path = '/' . $userId . '/files/Notes';
210
		if ($this->root->nodeExists($path)) {
211
			$folder = $this->root->get($path);
212
		} else {
213
			$folder = $this->root->newFolder($path);
214
		}
215 11
		return $folder;
216 11
	}
217 11
218 11
	/**
219 11
	 * get path of file and the title.txt and check if they are the same
220
	 * file. If not the title needs to be renamed
221
	 *
222 11
	 * @param Folder $folder a folder to the notes directory
223
	 * @param string $title the filename which should be used
224
	 * @param string $extension the extension which should be used
225
	 * @param int $id the id of the note for which the title should be generated
226
	 * used to see if the file itself has the title and not a different file for
227
	 * checking for filename collisions
228
	 * @return string the resolved filename to prevent overwriting different
229
	 * files with the same title
230
	 */
231
	private function generateFileName(Folder $folder, $title, $extension, $id) {
232
		$path = $title . '.' . $extension;
233
234
		// if file does not exist, that name has not been taken. Similar we don't
235
		// need to handle file collisions if it is the filename did not change
236
		if (!$folder->nodeExists($path) || $folder->get($path)->getId() === $id) {
237
			return $path;
238
		} else {
239 4
			// increments name (2) to name (3)
240 4
			$match = \preg_match('/\((?P<id>\d+)\)$/', $title, $matches);
241
			if ($match) {
242
				$newId = ((int) $matches['id']) + 1;
243
				$newTitle = \preg_replace('/(.*)\s\((\d+)\)$/',
244 4
					'$1 (' . $newId . ')', $title);
245 4
			} else {
246
				$newTitle = $title . ' (2)';
247
			}
248 3
			return $this->generateFileName($folder, $newTitle, $extension, $id);
249 3
		}
250 3
	}
251 3
252 3
	/**
253 3
	 * test if file is a note
254 3
	 *
255
	 * @param \OCP\Files\File $file
256 3
	 * @return bool
257
	 */
258
	private function isNote($file) {
259
		$allowedExtensions = ['txt', 'org', 'markdown', 'md', 'note'];
260
261
		if ($file->getType() !== 'file') {
262
			return false;
263
		}
264
		if (!\in_array(
265
			\pathinfo($file->getName(), PATHINFO_EXTENSION),
266 7
			$allowedExtensions
267 7
		)) {
268
			return false;
269 7
		}
270 7
271 7
		return true;
272
	}
273
}
274