diff options
Diffstat (limited to 'application/netscape/NetscapeBookmarkUtils.php')
-rw-r--r-- | application/netscape/NetscapeBookmarkUtils.php | 212 |
1 files changed, 108 insertions, 104 deletions
diff --git a/application/netscape/NetscapeBookmarkUtils.php b/application/netscape/NetscapeBookmarkUtils.php index 28665941..b83f16f8 100644 --- a/application/netscape/NetscapeBookmarkUtils.php +++ b/application/netscape/NetscapeBookmarkUtils.php | |||
@@ -6,56 +6,69 @@ use DateTime; | |||
6 | use DateTimeZone; | 6 | use DateTimeZone; |
7 | use Exception; | 7 | use Exception; |
8 | use Katzgrau\KLogger\Logger; | 8 | use Katzgrau\KLogger\Logger; |
9 | use Psr\Http\Message\UploadedFileInterface; | ||
9 | use Psr\Log\LogLevel; | 10 | use Psr\Log\LogLevel; |
10 | use Shaarli\Bookmark\LinkDB; | 11 | use Shaarli\Bookmark\Bookmark; |
12 | use Shaarli\Bookmark\BookmarkServiceInterface; | ||
11 | use Shaarli\Config\ConfigManager; | 13 | use Shaarli\Config\ConfigManager; |
14 | use Shaarli\Formatter\BookmarkFormatter; | ||
12 | use Shaarli\History; | 15 | use Shaarli\History; |
13 | use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser; | 16 | use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser; |
14 | 17 | ||
15 | /** | 18 | /** |
16 | * Utilities to import and export bookmarks using the Netscape format | 19 | * Utilities to import and export bookmarks using the Netscape format |
17 | * TODO: Not static, use a container. | ||
18 | */ | 20 | */ |
19 | class NetscapeBookmarkUtils | 21 | class NetscapeBookmarkUtils |
20 | { | 22 | { |
23 | /** @var BookmarkServiceInterface */ | ||
24 | protected $bookmarkService; | ||
25 | |||
26 | /** @var ConfigManager */ | ||
27 | protected $conf; | ||
28 | |||
29 | /** @var History */ | ||
30 | protected $history; | ||
31 | |||
32 | public function __construct(BookmarkServiceInterface $bookmarkService, ConfigManager $conf, History $history) | ||
33 | { | ||
34 | $this->bookmarkService = $bookmarkService; | ||
35 | $this->conf = $conf; | ||
36 | $this->history = $history; | ||
37 | } | ||
21 | 38 | ||
22 | /** | 39 | /** |
23 | * Filters links and adds Netscape-formatted fields | 40 | * Filters bookmarks and adds Netscape-formatted fields |
24 | * | 41 | * |
25 | * Added fields: | 42 | * Added fields: |
26 | * - timestamp link addition date, using the Unix epoch format | 43 | * - timestamp link addition date, using the Unix epoch format |
27 | * - taglist comma-separated tag list | 44 | * - taglist comma-separated tag list |
28 | * | 45 | * |
29 | * @param LinkDB $linkDb Link datastore | 46 | * @param BookmarkFormatter $formatter instance |
30 | * @param string $selection Which links to export: (all|private|public) | 47 | * @param string $selection Which bookmarks to export: (all|private|public) |
31 | * @param bool $prependNoteUrl Prepend note permalinks with the server's URL | 48 | * @param bool $prependNoteUrl Prepend note permalinks with the server's URL |
32 | * @param string $indexUrl Absolute URL of the Shaarli index page | 49 | * @param string $indexUrl Absolute URL of the Shaarli index page |
33 | * | 50 | * |
34 | * @throws Exception Invalid export selection | 51 | * @return array The bookmarks to be exported, with additional fields |
35 | * | 52 | * |
36 | * @return array The links to be exported, with additional fields | 53 | * @throws Exception Invalid export selection |
37 | */ | 54 | */ |
38 | public static function filterAndFormat($linkDb, $selection, $prependNoteUrl, $indexUrl) | 55 | public function filterAndFormat( |
39 | { | 56 | $formatter, |
57 | $selection, | ||
58 | $prependNoteUrl, | ||
59 | $indexUrl | ||
60 | ) { | ||
40 | // see tpl/export.html for possible values | 61 | // see tpl/export.html for possible values |
41 | if (!in_array($selection, array('all', 'public', 'private'))) { | 62 | if (!in_array($selection, array('all', 'public', 'private'))) { |
42 | throw new Exception(t('Invalid export selection:') . ' "' . $selection . '"'); | 63 | throw new Exception(t('Invalid export selection:') . ' "' . $selection . '"'); |
43 | } | 64 | } |
44 | 65 | ||
45 | $bookmarkLinks = array(); | 66 | $bookmarkLinks = array(); |
46 | foreach ($linkDb as $link) { | 67 | foreach ($this->bookmarkService->search([], $selection) as $bookmark) { |
47 | if ($link['private'] != 0 && $selection == 'public') { | 68 | $link = $formatter->format($bookmark); |
48 | continue; | 69 | $link['taglist'] = implode(',', $bookmark->getTags()); |
49 | } | 70 | if ($bookmark->isNote() && $prependNoteUrl) { |
50 | if ($link['private'] == 0 && $selection == 'private') { | 71 | $link['url'] = rtrim($indexUrl, '/') . '/' . ltrim($link['url'], '/'); |
51 | continue; | ||
52 | } | ||
53 | $date = $link['created']; | ||
54 | $link['timestamp'] = $date->getTimestamp(); | ||
55 | $link['taglist'] = str_replace(' ', ',', $link['tags']); | ||
56 | |||
57 | if (is_note($link['url']) && $prependNoteUrl) { | ||
58 | $link['url'] = $indexUrl . $link['url']; | ||
59 | } | 72 | } |
60 | 73 | ||
61 | $bookmarkLinks[] = $link; | 74 | $bookmarkLinks[] = $link; |
@@ -65,66 +78,28 @@ class NetscapeBookmarkUtils | |||
65 | } | 78 | } |
66 | 79 | ||
67 | /** | 80 | /** |
68 | * Generates an import status summary | ||
69 | * | ||
70 | * @param string $filename name of the file to import | ||
71 | * @param int $filesize size of the file to import | ||
72 | * @param int $importCount how many links were imported | ||
73 | * @param int $overwriteCount how many links were overwritten | ||
74 | * @param int $skipCount how many links were skipped | ||
75 | * @param int $duration how many seconds did the import take | ||
76 | * | ||
77 | * @return string Summary of the bookmark import status | ||
78 | */ | ||
79 | private static function importStatus( | ||
80 | $filename, | ||
81 | $filesize, | ||
82 | $importCount = 0, | ||
83 | $overwriteCount = 0, | ||
84 | $skipCount = 0, | ||
85 | $duration = 0 | ||
86 | ) { | ||
87 | $status = sprintf(t('File %s (%d bytes) '), $filename, $filesize); | ||
88 | if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) { | ||
89 | $status .= t('has an unknown file format. Nothing was imported.'); | ||
90 | } else { | ||
91 | $status .= vsprintf( | ||
92 | t( | ||
93 | 'was successfully processed in %d seconds: ' | ||
94 | . '%d links imported, %d links overwritten, %d links skipped.' | ||
95 | ), | ||
96 | [$duration, $importCount, $overwriteCount, $skipCount] | ||
97 | ); | ||
98 | } | ||
99 | return $status; | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * Imports Web bookmarks from an uploaded Netscape bookmark dump | 81 | * Imports Web bookmarks from an uploaded Netscape bookmark dump |
104 | * | 82 | * |
105 | * @param array $post Server $_POST parameters | 83 | * @param array $post Server $_POST parameters |
106 | * @param array $files Server $_FILES parameters | 84 | * @param UploadedFileInterface $file File in PSR-7 object format |
107 | * @param LinkDB $linkDb Loaded LinkDB instance | ||
108 | * @param ConfigManager $conf instance | ||
109 | * @param History $history History instance | ||
110 | * | 85 | * |
111 | * @return string Summary of the bookmark import status | 86 | * @return string Summary of the bookmark import status |
112 | */ | 87 | */ |
113 | public static function import($post, $files, $linkDb, $conf, $history) | 88 | public function import($post, UploadedFileInterface $file) |
114 | { | 89 | { |
115 | $start = time(); | 90 | $start = time(); |
116 | $filename = $files['filetoupload']['name']; | 91 | $filename = $file->getClientFilename(); |
117 | $filesize = $files['filetoupload']['size']; | 92 | $filesize = $file->getSize(); |
118 | $data = file_get_contents($files['filetoupload']['tmp_name']); | 93 | $data = (string) $file->getStream(); |
119 | 94 | ||
120 | if (preg_match('/<!DOCTYPE NETSCAPE-Bookmark-file-1>/i', $data) === 0) { | 95 | if (preg_match('/<!DOCTYPE NETSCAPE-Bookmark-file-1>/i', $data) === 0) { |
121 | return self::importStatus($filename, $filesize); | 96 | return $this->importStatus($filename, $filesize); |
122 | } | 97 | } |
123 | 98 | ||
124 | // Overwrite existing links? | 99 | // Overwrite existing bookmarks? |
125 | $overwrite = !empty($post['overwrite']); | 100 | $overwrite = !empty($post['overwrite']); |
126 | 101 | ||
127 | // Add tags to all imported links? | 102 | // Add tags to all imported bookmarks? |
128 | if (empty($post['default_tags'])) { | 103 | if (empty($post['default_tags'])) { |
129 | $defaultTags = array(); | 104 | $defaultTags = array(); |
130 | } else { | 105 | } else { |
@@ -134,18 +109,18 @@ class NetscapeBookmarkUtils | |||
134 | ); | 109 | ); |
135 | } | 110 | } |
136 | 111 | ||
137 | // links are imported as public by default | 112 | // bookmarks are imported as public by default |
138 | $defaultPrivacy = 0; | 113 | $defaultPrivacy = 0; |
139 | 114 | ||
140 | $parser = new NetscapeBookmarkParser( | 115 | $parser = new NetscapeBookmarkParser( |
141 | true, // nested tag support | 116 | true, // nested tag support |
142 | $defaultTags, // additional user-specified tags | 117 | $defaultTags, // additional user-specified tags |
143 | strval(1 - $defaultPrivacy), // defaultPub = 1 - defaultPrivacy | 118 | strval(1 - $defaultPrivacy), // defaultPub = 1 - defaultPrivacy |
144 | $conf->get('resource.data_dir') // log path, will be overridden | 119 | $this->conf->get('resource.data_dir') // log path, will be overridden |
145 | ); | 120 | ); |
146 | $logger = new Logger( | 121 | $logger = new Logger( |
147 | $conf->get('resource.data_dir'), | 122 | $this->conf->get('resource.data_dir'), |
148 | !$conf->get('dev.debug') ? LogLevel::INFO : LogLevel::DEBUG, | 123 | !$this->conf->get('dev.debug') ? LogLevel::INFO : LogLevel::DEBUG, |
149 | [ | 124 | [ |
150 | 'prefix' => 'import.', | 125 | 'prefix' => 'import.', |
151 | 'extension' => 'log', | 126 | 'extension' => 'log', |
@@ -164,22 +139,18 @@ class NetscapeBookmarkUtils | |||
164 | // use value from the imported file | 139 | // use value from the imported file |
165 | $private = $bkm['pub'] == '1' ? 0 : 1; | 140 | $private = $bkm['pub'] == '1' ? 0 : 1; |
166 | } elseif ($post['privacy'] == 'private') { | 141 | } elseif ($post['privacy'] == 'private') { |
167 | // all imported links are private | 142 | // all imported bookmarks are private |
168 | $private = 1; | 143 | $private = 1; |
169 | } elseif ($post['privacy'] == 'public') { | 144 | } elseif ($post['privacy'] == 'public') { |
170 | // all imported links are public | 145 | // all imported bookmarks are public |
171 | $private = 0; | 146 | $private = 0; |
172 | } | 147 | } |
173 | 148 | ||
174 | $newLink = array( | 149 | $link = $this->bookmarkService->findByUrl($bkm['uri']); |
175 | 'title' => $bkm['title'], | 150 | $existingLink = $link !== null; |
176 | 'url' => $bkm['uri'], | 151 | if (! $existingLink) { |
177 | 'description' => $bkm['note'], | 152 | $link = new Bookmark(); |
178 | 'private' => $private, | 153 | } |
179 | 'tags' => $bkm['tags'] | ||
180 | ); | ||
181 | |||
182 | $existingLink = $linkDb->getLinkFromUrl($bkm['uri']); | ||
183 | 154 | ||
184 | if ($existingLink !== false) { | 155 | if ($existingLink !== false) { |
185 | if ($overwrite === false) { | 156 | if ($overwrite === false) { |
@@ -188,32 +159,30 @@ class NetscapeBookmarkUtils | |||
188 | continue; | 159 | continue; |
189 | } | 160 | } |
190 | 161 | ||
191 | // Overwrite an existing link, keep its date | 162 | $link->setUpdated(new DateTime()); |
192 | $newLink['id'] = $existingLink['id']; | ||
193 | $newLink['created'] = $existingLink['created']; | ||
194 | $newLink['updated'] = new DateTime(); | ||
195 | $newLink['shorturl'] = $existingLink['shorturl']; | ||
196 | $linkDb[$existingLink['id']] = $newLink; | ||
197 | $importCount++; | ||
198 | $overwriteCount++; | 163 | $overwriteCount++; |
199 | continue; | 164 | } else { |
165 | $newLinkDate = new DateTime('@' . strval($bkm['time'])); | ||
166 | $newLinkDate->setTimezone(new DateTimeZone(date_default_timezone_get())); | ||
167 | $link->setCreated($newLinkDate); | ||
200 | } | 168 | } |
201 | 169 | ||
202 | // Add a new link - @ used for UNIX timestamps | 170 | $link->setTitle($bkm['title']); |
203 | $newLinkDate = new DateTime('@' . strval($bkm['time'])); | 171 | $link->setUrl($bkm['uri'], $this->conf->get('security.allowed_protocols')); |
204 | $newLinkDate->setTimezone(new DateTimeZone(date_default_timezone_get())); | 172 | $link->setDescription($bkm['note']); |
205 | $newLink['created'] = $newLinkDate; | 173 | $link->setPrivate($private); |
206 | $newLink['id'] = $linkDb->getNextId(); | 174 | $link->setTagsString($bkm['tags']); |
207 | $newLink['shorturl'] = link_small_hash($newLink['created'], $newLink['id']); | 175 | |
208 | $linkDb[$newLink['id']] = $newLink; | 176 | $this->bookmarkService->addOrSet($link, false); |
209 | $importCount++; | 177 | $importCount++; |
210 | } | 178 | } |
211 | 179 | ||
212 | $linkDb->save($conf->get('resource.page_cache')); | 180 | $this->bookmarkService->save(); |
213 | $history->importLinks(); | 181 | $this->history->importLinks(); |
214 | 182 | ||
215 | $duration = time() - $start; | 183 | $duration = time() - $start; |
216 | return self::importStatus( | 184 | |
185 | return $this->importStatus( | ||
217 | $filename, | 186 | $filename, |
218 | $filesize, | 187 | $filesize, |
219 | $importCount, | 188 | $importCount, |
@@ -222,4 +191,39 @@ class NetscapeBookmarkUtils | |||
222 | $duration | 191 | $duration |
223 | ); | 192 | ); |
224 | } | 193 | } |
194 | |||
195 | /** | ||
196 | * Generates an import status summary | ||
197 | * | ||
198 | * @param string $filename name of the file to import | ||
199 | * @param int $filesize size of the file to import | ||
200 | * @param int $importCount how many bookmarks were imported | ||
201 | * @param int $overwriteCount how many bookmarks were overwritten | ||
202 | * @param int $skipCount how many bookmarks were skipped | ||
203 | * @param int $duration how many seconds did the import take | ||
204 | * | ||
205 | * @return string Summary of the bookmark import status | ||
206 | */ | ||
207 | protected function importStatus( | ||
208 | $filename, | ||
209 | $filesize, | ||
210 | $importCount = 0, | ||
211 | $overwriteCount = 0, | ||
212 | $skipCount = 0, | ||
213 | $duration = 0 | ||
214 | ) { | ||
215 | $status = sprintf(t('File %s (%d bytes) '), $filename, $filesize); | ||
216 | if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) { | ||
217 | $status .= t('has an unknown file format. Nothing was imported.'); | ||
218 | } else { | ||
219 | $status .= vsprintf( | ||
220 | t( | ||
221 | 'was successfully processed in %d seconds: ' | ||
222 | . '%d bookmarks imported, %d bookmarks overwritten, %d bookmarks skipped.' | ||
223 | ), | ||
224 | [$duration, $importCount, $overwriteCount, $skipCount] | ||
225 | ); | ||
226 | } | ||
227 | return $status; | ||
228 | } | ||
225 | } | 229 | } |