diff options
Diffstat (limited to 'application/netscape')
-rw-r--r-- | application/netscape/NetscapeBookmarkUtils.php | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/application/netscape/NetscapeBookmarkUtils.php b/application/netscape/NetscapeBookmarkUtils.php new file mode 100644 index 00000000..2fb1a4a6 --- /dev/null +++ b/application/netscape/NetscapeBookmarkUtils.php | |||
@@ -0,0 +1,225 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Shaarli\Netscape; | ||
4 | |||
5 | use DateTime; | ||
6 | use DateTimeZone; | ||
7 | use Exception; | ||
8 | use Katzgrau\KLogger\Logger; | ||
9 | use Psr\Log\LogLevel; | ||
10 | use Shaarli\Bookmark\LinkDB; | ||
11 | use Shaarli\Config\ConfigManager; | ||
12 | use Shaarli\History; | ||
13 | use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser; | ||
14 | |||
15 | /** | ||
16 | * Utilities to import and export bookmarks using the Netscape format | ||
17 | * TODO: Not static, use a container. | ||
18 | */ | ||
19 | class NetscapeBookmarkUtils | ||
20 | { | ||
21 | |||
22 | /** | ||
23 | * Filters links and adds Netscape-formatted fields | ||
24 | * | ||
25 | * Added fields: | ||
26 | * - timestamp link addition date, using the Unix epoch format | ||
27 | * - taglist comma-separated tag list | ||
28 | * | ||
29 | * @param LinkDB $linkDb Link datastore | ||
30 | * @param string $selection Which links to export: (all|private|public) | ||
31 | * @param bool $prependNoteUrl Prepend note permalinks with the server's URL | ||
32 | * @param string $indexUrl Absolute URL of the Shaarli index page | ||
33 | * | ||
34 | * @throws Exception Invalid export selection | ||
35 | * | ||
36 | * @return array The links to be exported, with additional fields | ||
37 | */ | ||
38 | public static function filterAndFormat($linkDb, $selection, $prependNoteUrl, $indexUrl) | ||
39 | { | ||
40 | // see tpl/export.html for possible values | ||
41 | if (!in_array($selection, array('all', 'public', 'private'))) { | ||
42 | throw new Exception(t('Invalid export selection:') . ' "' . $selection . '"'); | ||
43 | } | ||
44 | |||
45 | $bookmarkLinks = array(); | ||
46 | foreach ($linkDb as $link) { | ||
47 | if ($link['private'] != 0 && $selection == 'public') { | ||
48 | continue; | ||
49 | } | ||
50 | if ($link['private'] == 0 && $selection == 'private') { | ||
51 | continue; | ||
52 | } | ||
53 | $date = $link['created']; | ||
54 | $link['timestamp'] = $date->getTimestamp(); | ||
55 | $link['taglist'] = str_replace(' ', ',', $link['tags']); | ||
56 | |||
57 | if (startsWith($link['url'], '?') && $prependNoteUrl) { | ||
58 | $link['url'] = $indexUrl . $link['url']; | ||
59 | } | ||
60 | |||
61 | $bookmarkLinks[] = $link; | ||
62 | } | ||
63 | |||
64 | return $bookmarkLinks; | ||
65 | } | ||
66 | |||
67 | /** | ||
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 | ||
104 | * | ||
105 | * @param array $post Server $_POST parameters | ||
106 | * @param array $files Server $_FILES parameters | ||
107 | * @param LinkDB $linkDb Loaded LinkDB instance | ||
108 | * @param ConfigManager $conf instance | ||
109 | * @param History $history History instance | ||
110 | * | ||
111 | * @return string Summary of the bookmark import status | ||
112 | */ | ||
113 | public static function import($post, $files, $linkDb, $conf, $history) | ||
114 | { | ||
115 | $start = time(); | ||
116 | $filename = $files['filetoupload']['name']; | ||
117 | $filesize = $files['filetoupload']['size']; | ||
118 | $data = file_get_contents($files['filetoupload']['tmp_name']); | ||
119 | |||
120 | if (preg_match('/<!DOCTYPE NETSCAPE-Bookmark-file-1>/i', $data) === 0) { | ||
121 | return self::importStatus($filename, $filesize); | ||
122 | } | ||
123 | |||
124 | // Overwrite existing links? | ||
125 | $overwrite = !empty($post['overwrite']); | ||
126 | |||
127 | // Add tags to all imported links? | ||
128 | if (empty($post['default_tags'])) { | ||
129 | $defaultTags = array(); | ||
130 | } else { | ||
131 | $defaultTags = preg_split( | ||
132 | '/[\s,]+/', | ||
133 | escape($post['default_tags']) | ||
134 | ); | ||
135 | } | ||
136 | |||
137 | // links are imported as public by default | ||
138 | $defaultPrivacy = 0; | ||
139 | |||
140 | $parser = new NetscapeBookmarkParser( | ||
141 | true, // nested tag support | ||
142 | $defaultTags, // additional user-specified tags | ||
143 | strval(1 - $defaultPrivacy), // defaultPub = 1 - defaultPrivacy | ||
144 | $conf->get('resource.data_dir') // log path, will be overridden | ||
145 | ); | ||
146 | $logger = new Logger( | ||
147 | $conf->get('resource.data_dir'), | ||
148 | !$conf->get('dev.debug') ? LogLevel::INFO : LogLevel::DEBUG, | ||
149 | [ | ||
150 | 'prefix' => 'import.', | ||
151 | 'extension' => 'log', | ||
152 | ] | ||
153 | ); | ||
154 | $parser->setLogger($logger); | ||
155 | $bookmarks = $parser->parseString($data); | ||
156 | |||
157 | $importCount = 0; | ||
158 | $overwriteCount = 0; | ||
159 | $skipCount = 0; | ||
160 | |||
161 | foreach ($bookmarks as $bkm) { | ||
162 | $private = $defaultPrivacy; | ||
163 | if (empty($post['privacy']) || $post['privacy'] == 'default') { | ||
164 | // use value from the imported file | ||
165 | $private = $bkm['pub'] == '1' ? 0 : 1; | ||
166 | } elseif ($post['privacy'] == 'private') { | ||
167 | // all imported links are private | ||
168 | $private = 1; | ||
169 | } elseif ($post['privacy'] == 'public') { | ||
170 | // all imported links are public | ||
171 | $private = 0; | ||
172 | } | ||
173 | |||
174 | $newLink = array( | ||
175 | 'title' => $bkm['title'], | ||
176 | 'url' => $bkm['uri'], | ||
177 | 'description' => $bkm['note'], | ||
178 | 'private' => $private, | ||
179 | 'tags' => $bkm['tags'] | ||
180 | ); | ||
181 | |||
182 | $existingLink = $linkDb->getLinkFromUrl($bkm['uri']); | ||
183 | |||
184 | if ($existingLink !== false) { | ||
185 | if ($overwrite === false) { | ||
186 | // Do not overwrite an existing link | ||
187 | $skipCount++; | ||
188 | continue; | ||
189 | } | ||
190 | |||
191 | // Overwrite an existing link, keep its date | ||
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++; | ||
199 | continue; | ||
200 | } | ||
201 | |||
202 | // Add a new link - @ used for UNIX timestamps | ||
203 | $newLinkDate = new DateTime('@' . strval($bkm['time'])); | ||
204 | $newLinkDate->setTimezone(new DateTimeZone(date_default_timezone_get())); | ||
205 | $newLink['created'] = $newLinkDate; | ||
206 | $newLink['id'] = $linkDb->getNextId(); | ||
207 | $newLink['shorturl'] = link_small_hash($newLink['created'], $newLink['id']); | ||
208 | $linkDb[$newLink['id']] = $newLink; | ||
209 | $importCount++; | ||
210 | } | ||
211 | |||
212 | $linkDb->save($conf->get('resource.page_cache')); | ||
213 | $history->importLinks(); | ||
214 | |||
215 | $duration = time() - $start; | ||
216 | return self::importStatus( | ||
217 | $filename, | ||
218 | $filesize, | ||
219 | $importCount, | ||
220 | $overwriteCount, | ||
221 | $skipCount, | ||
222 | $duration | ||
223 | ); | ||
224 | } | ||
225 | } | ||