Completed
Pull Request — master (#248)
by
unknown
04:04
created

NotesService::get()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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