Completed
Pull Request — master (#196)
by Janez
06:00
created

NotesService::getAll()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 13
ccs 10
cts 10
cp 1
rs 9.4285
cc 3
eloc 8
nc 3
nop 1
crap 3
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
        $notes = [];
48
49 1
        foreach($files as $file) {
50 1
            if($this->isNote($file)) {
51 1
                $notes[] = Note::fromFile($file);
52 1
            }
53 1
        }
54
55 1
        return $notes;
56
    }
57
58
59
    /**
60
     * Used to get a single note by id
61
     * @param int $id the id of the note to get
62
     * @param string $userId
63
     * @throws NoteDoesNotExistException if note does not exist
64
     * @return Note
65
     */
66 3
    public function get ($id, $userId) {
67 3
        $folder = $this->getFolderForUser($userId);
68 3
        return Note::fromFile($this->getFileById($folder, $id));
69
    }
70
71
72
    /**
73
     * Creates a note and returns the empty note
74
     * @param string $userId
75
     * @see update for setting note content
76
     * @return Note the newly created note
77
     */
78 2
    public function create ($userId) {
79 2
        $title = $this->l10n->t('New note');
80 2
        $folder = $this->getFolderForUser($userId);
81
82
        // check new note exists already and we need to number it
83
        // pass -1 because no file has id -1 and that will ensure
84
        // to only return filenames that dont yet exist
85 2
        $path = $this->generateFileName($folder, $title, "txt", -1);
86 2
        $file = $folder->newFile($path);
87
88 2
        return Note::fromFile($file);
89
    }
90
91
92
    /**
93
     * Updates a note. Be sure to check the returned note since the title is
94
     * dynamically generated and filename conflicts are resolved
95
     * @param int $id the id of the note used to update
96
     * @param string $content the content which will be written into the note
97
     * the title is generated from the first line of the content
98
     * @throws NoteDoesNotExistException if note does not exist
99
     * @return \OCA\Notes\Db\Note the updated note
100
     */
101 2
    public function update ($id, $content, $userId){
102 2
        $folder = $this->getFolderForUser($userId);
103 2
        $file = $this->getFileById($folder, $id);
104
105
        // generate content from the first line of the title
106 2
        $splitContent = explode("\n", $content);
107 2
        $title = $splitContent[0];
108
109 2
        if(!$title) {
110 1
            $title = $this->l10n->t('New note');
111 1
        }
112
113
        // prevent directory traversal
114 2
        $title = str_replace(array('/', '\\'), '',  $title);
115
        // replace non-alphanumeric characters with an underscore
116 2
        $title = preg_replace('/[\s\W]+/', '_', $title);
117
        // remove any underscores from the beginning and the end
118 2
        $title = trim($title, '_');
119
        // using a maximum of 100 chars should be enough
120 2
        $title = substr($title, 0, 100);
121
122
        // generate filename if there were collisions
123 2
        $currentFilePath = $file->getPath();
124 2
        $basePath = '/' . $userId . '/files/Notes/';
125 2
        $fileExtension = pathinfo($file->getName(), PATHINFO_EXTENSION);
126 2
        $newFilePath = $basePath . $this->generateFileName($folder, $title, $fileExtension, $id);
127
128
        // if the current path is not the new path, the file has to be renamed
129 1
        if($currentFilePath !== $newFilePath) {
130 1
            $file->move($newFilePath);
131 1
        }
132
133 1
        $file->putContent($content);
134
135 1
        \OCP\Util::writeLog('notes', print_r(Note::fromFile($file), true), \OCP\Util::ERROR);
136
137 1
        return Note::fromFile($file);
138
    }
139
140
141
    /**
142
     * Deletes a note
143
     * @param int $id the id of the note which should be deleted
144
     * @param string $userId
145
     * @throws NoteDoesNotExistException if note does not
146
     * exist
147
     */
148 3
    public function delete ($id, $userId) {
149 3
        $folder = $this->getFolderForUser($userId);
150 3
        $file = $this->getFileById($folder, $id);
151 1
        $file->delete();
152 1
    }
153
154
155
    /**
156
     * @param Folder $folder
157
     * @param int $id
158
     * @throws NoteDoesNotExistException
159
     * @return \OCP\Files\File
160
     */
161 8
    private function getFileById ($folder, $id) {
162 8
        $file = $folder->getById($id);
163
164 8
        if(count($file) <= 0 || !$this->isNote($file[0])) {
165 4
            throw new NoteDoesNotExistException();
166
        }
167
168 4
        return $file[0];
169
    }
170
171
172
    /**
173
     * @param string $userId the user id
174
     * @return Folder
175
     */
176 11
    private function getFolderForUser ($userId) {
177 11
        $path = '/' . $userId . '/files/Notes';
178 11
        if ($this->root->nodeExists($path)) {
179 11
            $folder = $this->root->get($path);
180 11
        } else {
181
            $folder = $this->root->newFolder($path);
182
        }
183 11
        return $folder;
184
    }
185
186
187
    /**
188
     * get path of file and the title.txt and check if they are the same
189
     * file. If not the title needs to be renamed
190
     *
191
     * @param Folder $folder a folder to the notes directory
192
     * @param string $title the filename which should be used
193
     * @param string $extension the extension which should be used
194
     * @param int $id the id of the note for which the title should be generated
195
     * used to see if the file itself has the title and not a different file for
196
     * checking for filename collisions
197
     * @return string the resolved filename to prevent overwriting different
198
     * files with the same title
199
     */
200 4
    private function generateFileName (Folder $folder, $title, $extension, $id) {
201 4
        $path = $title . '.' . $extension;
202
203
        // if file does not exist, that name has not been taken. Similar we don't
204
        // need to handle file collisions if it is the filename did not change
205 4
        if (!$folder->nodeExists($path) || $folder->get($path)->getId() === $id) {
206 3
            return $path;
207
        } else {
208
            // increments name (2) to name (3)
209 2
            $match = preg_match('/\((?P<id>\d+)\)$/', $title, $matches);
210 2
            if($match) {
211 2
                $newId = ((int) $matches['id']) + 1;
212 2
                $newTitle = preg_replace('/(.*)\s\((\d+)\)$/',
213 2
                    '$1 (' . $newId . ')', $title);
214 2
            } else {
215 2
                $newTitle = $title . ' (2)';
216
            }
217 2
            return $this->generateFileName($folder, $newTitle, $extension, $id);
218
        }
219
    }
220
221
    /**
222
     * test if file is a note
223
     *
224
     * @param \OCP\Files\File $file
225
     * @return bool
226
     */
227 7
    private function isNote($file) {
228 7
        $allowedExtensions = ['txt', 'org', 'markdown', 'md', 'note'];
229
230 7
        if($file->getType() !== 'file') return false;
231 7
        if(!in_array(
232 7
            pathinfo($file->getName(), PATHINFO_EXTENSION),
233
            $allowedExtensions
234 7
        )) return false;
235
236 5
        return true;
237
    }
238
239
}
240