diff options
68 files changed, 1278 insertions, 358 deletions
@@ -17,7 +17,7 @@ composer.lock | |||
17 | vendor/ | 17 | vendor/ |
18 | 18 | ||
19 | # Release archives | 19 | # Release archives |
20 | *.tar | 20 | *.tar.gz |
21 | *.zip | 21 | *.zip |
22 | 22 | ||
23 | # Development and test resources | 23 | # Development and test resources |
diff --git a/.travis.yml b/.travis.yml index 9ffb3d00..6ff1b20f 100644 --- a/.travis.yml +++ b/.travis.yml | |||
@@ -4,6 +4,7 @@ cache: | |||
4 | directories: | 4 | directories: |
5 | - $HOME/.composer/cache | 5 | - $HOME/.composer/cache |
6 | php: | 6 | php: |
7 | - 7.1 | ||
7 | - 7.0 | 8 | - 7.0 |
8 | - 5.6 | 9 | - 5.6 |
9 | - 5.5 | 10 | - 5.5 |
diff --git a/CHANGELOG.md b/CHANGELOG.md index d42d6a75..21d5436c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md | |||
@@ -5,7 +5,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) | |||
5 | and this project adheres to [Semantic Versioning](http://semver.org/). | 5 | and this project adheres to [Semantic Versioning](http://semver.org/). |
6 | 6 | ||
7 | 7 | ||
8 | ## [v0.8.1](https://github.com/shaarli/Shaarli/releases/tag/v0.8.1) - UNPUBLISHED | 8 | ## [v0.9.0](https://github.com/shaarli/Shaarli/releases/tag/v0.9.0) - UNPUBLISHED |
9 | |||
10 | ### Added | ||
11 | |||
12 | ### Changed | ||
13 | |||
14 | ### Fixed | ||
15 | |||
16 | |||
17 | ## [v0.8.1](https://github.com/shaarli/Shaarli/releases/tag/v0.8.1) - 2016-12-12 | ||
18 | |||
19 | > Note: this version will create an automatic backup of your database if anything goes wrong. | ||
20 | |||
9 | ### Added | 21 | ### Added |
10 | - Add CHANGELOG.md to track the whole project's history | 22 | - Add CHANGELOG.md to track the whole project's history |
11 | - Enable Composer cache for Travis builds | 23 | - Enable Composer cache for Travis builds |
@@ -13,20 +25,39 @@ and this project adheres to [Semantic Versioning](http://semver.org/). | |||
13 | - Plugins: | 25 | - Plugins: |
14 | - Add an [Isso](https://posativ.org/isso/) plugin to enable user comments on permalinks | 26 | - Add an [Isso](https://posativ.org/isso/) plugin to enable user comments on permalinks |
15 | - Allow defining init functions, e.g. for performing checks and error processing | 27 | - Allow defining init functions, e.g. for performing checks and error processing |
16 | 28 | - Add a Piwik plugin for analytics. | |
17 | ### Changed | 29 | - Markdown: add warning notice regarding HTML rendering |
18 | - Cleanup `{loop}` declarations in templates | 30 | - Meta tag to *not* send the referrer to external resources. |
31 | |||
32 | ### Changed | ||
33 | - Link ID complete refactoring: | ||
34 | - Links now have a numeric ID instead of dates | ||
35 | - Short URLs are now created once and can't change over time (previous URL are kept) | ||
36 | - Templates: | ||
37 | - Changed placeholder behaviour for: `buttons_toolbar`, `fields_toolbar` and `action_plugin` | ||
38 | - Cleanup `{loop}` declarations in templates | ||
39 | - Tools: hide Firefox Social button when not in HTTPS | ||
40 | - Firefox Social: show Shaarli's title when shaaring using Firefox Social | ||
19 | - Release archives now have the same structure as GitHub-generated archives: | 41 | - Release archives now have the same structure as GitHub-generated archives: |
20 | - archives contain a `Shaarli` directory, itself containing sources + dependencies | 42 | - archives contain a `Shaarli` directory, itself containing sources + dependencies |
21 | - the tarball is now gzipped | 43 | - the tarball is now gzipped |
44 | - Plugins: | ||
45 | - Markdown: Parsedown library is now imported through Composer | ||
22 | - Minor code cleanup: PHPDoc, spelling, unused variables, etc. | 46 | - Minor code cleanup: PHPDoc, spelling, unused variables, etc. |
47 | - Docker: explicitly set the maximum file upload size to 10 MiB | ||
23 | 48 | ||
24 | ### Fixed | 49 | ### Fixed |
25 | - Fix the server `<self>` value in Atom/RSS feeds | 50 | - Fix the server `<self>` value in Atom/RSS feeds |
26 | - Plugins: | 51 | - Plugins: |
27 | - Tools: only display parameter description when it exists | 52 | - Tools: only display parameter description when it exists |
28 | - archive.org: do not propose archival of private notes | 53 | - archive.org: do not propose archival of private notes |
54 | - Markdown: | ||
55 | - render links properly in code blocks | ||
56 | - bug regarding the `nomarkdown` tag | ||
57 | - W3C compliance | ||
29 | - Use absolute URL for hashtags in RSS and ATOM feeds | 58 | - Use absolute URL for hashtags in RSS and ATOM feeds |
59 | - Docker: specify the location of the favicon | ||
60 | - ATOM feed: remove new line between content tag and data | ||
30 | 61 | ||
31 | ### Security | 62 | ### Security |
32 | - Allow whitelisting trusted IPs, else continue banning clients upon login failure | 63 | - Allow whitelisting trusted IPs, else continue banning clients upon login failure |
@@ -72,6 +72,10 @@ Files: plugins/wallabag/wallabag.png | |||
72 | License: MIT License (http://opensource.org/licenses/MIT) | 72 | License: MIT License (http://opensource.org/licenses/MIT) |
73 | Copyright: (C) 2015 Nicolas Lœuillet - https://github.com/wallabag/wallabag | 73 | Copyright: (C) 2015 Nicolas Lœuillet - https://github.com/wallabag/wallabag |
74 | 74 | ||
75 | Files: plugins/markdown/Parsedown.php | ||
76 | License: MIT License (http://opensource.org/licenses/MIT) | ||
77 | Copyright: (C) 2015 Emanuil Rusev - https://github.com/erusev/parsedown | ||
78 | |||
75 | Files: tpl/default/img/sad_star.png | 79 | Files: tpl/default/img/sad_star.png |
76 | License: MIT License (http://opensource.org/licenses/MIT) | 80 | License: MIT License (http://opensource.org/licenses/MIT) |
77 | Copyright: (C) 2015 kalvn - https://github.com/kalvn/Shaarli-Material | 81 | Copyright: (C) 2015 kalvn - https://github.com/kalvn/Shaarli-Material |
diff --git a/application/.htaccess b/application/.htaccess index b584d98c..f601c1ee 100644 --- a/application/.htaccess +++ b/application/.htaccess | |||
@@ -1,2 +1,13 @@ | |||
1 | Allow from none | 1 | <IfModule version_module> |
2 | Deny from all | 2 | <IfVersion >= 2.4> |
3 | Require all denied | ||
4 | </IfVersion> | ||
5 | <IfVersion < 2.4> | ||
6 | Allow from none | ||
7 | Deny from all | ||
8 | </IfVersion> | ||
9 | </IfModule> | ||
10 | |||
11 | <IfModule !version_module> | ||
12 | Require all denied | ||
13 | </IfModule> | ||
diff --git a/application/FeedBuilder.php b/application/FeedBuilder.php index 4036a7cc..b0aa5764 100644 --- a/application/FeedBuilder.php +++ b/application/FeedBuilder.php | |||
@@ -156,12 +156,12 @@ class FeedBuilder | |||
156 | $link['description'] = format_description($link['description'], '', $pageaddr); | 156 | $link['description'] = format_description($link['description'], '', $pageaddr); |
157 | $link['description'] .= PHP_EOL .'<br>— '. $permalink; | 157 | $link['description'] .= PHP_EOL .'<br>— '. $permalink; |
158 | 158 | ||
159 | $pubDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']); | 159 | $pubDate = $link['created']; |
160 | $link['pub_iso_date'] = $this->getIsoDate($pubDate); | 160 | $link['pub_iso_date'] = $this->getIsoDate($pubDate); |
161 | 161 | ||
162 | // atom:entry elements MUST contain exactly one atom:updated element. | 162 | // atom:entry elements MUST contain exactly one atom:updated element. |
163 | if (!empty($link['updated'])) { | 163 | if (!empty($link['updated'])) { |
164 | $upDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['updated']); | 164 | $upDate = $link['updated']; |
165 | $link['up_iso_date'] = $this->getIsoDate($upDate, DateTime::ATOM); | 165 | $link['up_iso_date'] = $this->getIsoDate($upDate, DateTime::ATOM); |
166 | } else { | 166 | } else { |
167 | $link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM);; | 167 | $link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM);; |
diff --git a/application/LinkDB.php b/application/LinkDB.php index c8b162b6..1e13286a 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php | |||
@@ -6,15 +6,15 @@ | |||
6 | * | 6 | * |
7 | * Example: | 7 | * Example: |
8 | * $myLinks = new LinkDB(); | 8 | * $myLinks = new LinkDB(); |
9 | * echo $myLinks['20110826_161819']['title']; | 9 | * echo $myLinks[350]['title']; |
10 | * foreach ($myLinks as $link) | 10 | * foreach ($myLinks as $link) |
11 | * echo $link['title'].' at url '.$link['url'].'; description:'.$link['description']; | 11 | * echo $link['title'].' at url '.$link['url'].'; description:'.$link['description']; |
12 | * | 12 | * |
13 | * Available keys: | 13 | * Available keys: |
14 | * - id: primary key, incremental integer identifier (persistent) | ||
14 | * - description: description of the entry | 15 | * - description: description of the entry |
15 | * - linkdate: creation date of this entry, format: YYYYMMDD_HHMMSS | 16 | * - created: creation date of this entry, DateTime object. |
16 | * (e.g.'20110914_192317') | 17 | * - updated: last modification date of this entry, DateTime object. |
17 | * - updated: last modification date of this entry, format: YYYYMMDD_HHMMSS | ||
18 | * - private: Is this link private? 0=no, other value=yes | 18 | * - private: Is this link private? 0=no, other value=yes |
19 | * - tags: tags attached to this entry (separated by spaces) | 19 | * - tags: tags attached to this entry (separated by spaces) |
20 | * - title Title of the link | 20 | * - title Title of the link |
@@ -22,11 +22,25 @@ | |||
22 | * Can be absolute or relative. | 22 | * Can be absolute or relative. |
23 | * Relative URLs are permalinks (e.g.'?m-ukcw') | 23 | * Relative URLs are permalinks (e.g.'?m-ukcw') |
24 | * - real_url Absolute processed URL. | 24 | * - real_url Absolute processed URL. |
25 | * - shorturl Permalink smallhash | ||
25 | * | 26 | * |
26 | * Implements 3 interfaces: | 27 | * Implements 3 interfaces: |
27 | * - ArrayAccess: behaves like an associative array; | 28 | * - ArrayAccess: behaves like an associative array; |
28 | * - Countable: there is a count() method; | 29 | * - Countable: there is a count() method; |
29 | * - Iterator: usable in foreach () loops. | 30 | * - Iterator: usable in foreach () loops. |
31 | * | ||
32 | * ID mechanism: | ||
33 | * ArrayAccess is implemented in a way that will allow to access a link | ||
34 | * with the unique identifier ID directly with $link[ID]. | ||
35 | * Note that it's not the real key of the link array attribute. | ||
36 | * This mechanism is in place to have persistent link IDs, | ||
37 | * even though the internal array is reordered by date. | ||
38 | * Example: | ||
39 | * - DB: link #1 (2010-01-01) link #2 (2016-01-01) | ||
40 | * - Order: #2 #1 | ||
41 | * - Import links containing: link #3 (2013-01-01) | ||
42 | * - New DB: link #1 (2010-01-01) link #2 (2016-01-01) link #3 (2013-01-01) | ||
43 | * - Real order: #2 #3 #1 | ||
30 | */ | 44 | */ |
31 | class LinkDB implements Iterator, Countable, ArrayAccess | 45 | class LinkDB implements Iterator, Countable, ArrayAccess |
32 | { | 46 | { |
@@ -47,11 +61,17 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
47 | // - value: associative array (keys: title, description...) | 61 | // - value: associative array (keys: title, description...) |
48 | private $links; | 62 | private $links; |
49 | 63 | ||
50 | // List of all recorded URLs (key=url, value=linkdate) | 64 | // List of all recorded URLs (key=url, value=link offset) |
51 | // for fast reserve search (url-->linkdate) | 65 | // for fast reserve search (url-->link offset) |
52 | private $urls; | 66 | private $urls; |
53 | 67 | ||
54 | // List of linkdate keys (for the Iterator interface implementation) | 68 | /** |
69 | * @var array List of all links IDS mapped with their array offset. | ||
70 | * Map: id->offset. | ||
71 | */ | ||
72 | protected $ids; | ||
73 | |||
74 | // List of offset keys (for the Iterator interface implementation) | ||
55 | private $keys; | 75 | private $keys; |
56 | 76 | ||
57 | // Position in the $this->keys array (for the Iterator interface) | 77 | // Position in the $this->keys array (for the Iterator interface) |
@@ -121,14 +141,26 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
121 | if (!$this->loggedIn) { | 141 | if (!$this->loggedIn) { |
122 | die('You are not authorized to add a link.'); | 142 | die('You are not authorized to add a link.'); |
123 | } | 143 | } |
124 | if (empty($value['linkdate']) || empty($value['url'])) { | 144 | if (!isset($value['id']) || empty($value['url'])) { |
125 | die('Internal Error: A link should always have a linkdate and URL.'); | 145 | die('Internal Error: A link should always have an id and URL.'); |
126 | } | 146 | } |
127 | if (empty($offset)) { | 147 | if ((! empty($offset) && ! is_int($offset)) || ! is_int($value['id'])) { |
128 | die('You must specify a key.'); | 148 | die('You must specify an integer as a key.'); |
149 | } | ||
150 | if (! empty($offset) && $offset !== $value['id']) { | ||
151 | die('Array offset and link ID must be equal.'); | ||
152 | } | ||
153 | |||
154 | // If the link exists, we reuse the real offset, otherwise new entry | ||
155 | $existing = $this->getLinkOffset($offset); | ||
156 | if ($existing !== null) { | ||
157 | $offset = $existing; | ||
158 | } else { | ||
159 | $offset = count($this->links); | ||
129 | } | 160 | } |
130 | $this->links[$offset] = $value; | 161 | $this->links[$offset] = $value; |
131 | $this->urls[$value['url']]=$offset; | 162 | $this->urls[$value['url']] = $offset; |
163 | $this->ids[$value['id']] = $offset; | ||
132 | } | 164 | } |
133 | 165 | ||
134 | /** | 166 | /** |
@@ -136,7 +168,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
136 | */ | 168 | */ |
137 | public function offsetExists($offset) | 169 | public function offsetExists($offset) |
138 | { | 170 | { |
139 | return array_key_exists($offset, $this->links); | 171 | return array_key_exists($this->getLinkOffset($offset), $this->links); |
140 | } | 172 | } |
141 | 173 | ||
142 | /** | 174 | /** |
@@ -148,9 +180,11 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
148 | // TODO: raise an exception | 180 | // TODO: raise an exception |
149 | die('You are not authorized to delete a link.'); | 181 | die('You are not authorized to delete a link.'); |
150 | } | 182 | } |
151 | $url = $this->links[$offset]['url']; | 183 | $realOffset = $this->getLinkOffset($offset); |
184 | $url = $this->links[$realOffset]['url']; | ||
152 | unset($this->urls[$url]); | 185 | unset($this->urls[$url]); |
153 | unset($this->links[$offset]); | 186 | unset($this->ids[$realOffset]); |
187 | unset($this->links[$realOffset]); | ||
154 | } | 188 | } |
155 | 189 | ||
156 | /** | 190 | /** |
@@ -158,7 +192,8 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
158 | */ | 192 | */ |
159 | public function offsetGet($offset) | 193 | public function offsetGet($offset) |
160 | { | 194 | { |
161 | return isset($this->links[$offset]) ? $this->links[$offset] : null; | 195 | $realOffset = $this->getLinkOffset($offset); |
196 | return isset($this->links[$realOffset]) ? $this->links[$realOffset] : null; | ||
162 | } | 197 | } |
163 | 198 | ||
164 | /** | 199 | /** |
@@ -166,7 +201,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
166 | */ | 201 | */ |
167 | public function current() | 202 | public function current() |
168 | { | 203 | { |
169 | return $this->links[$this->keys[$this->position]]; | 204 | return $this[$this->keys[$this->position]]; |
170 | } | 205 | } |
171 | 206 | ||
172 | /** | 207 | /** |
@@ -192,8 +227,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
192 | */ | 227 | */ |
193 | public function rewind() | 228 | public function rewind() |
194 | { | 229 | { |
195 | $this->keys = array_keys($this->links); | 230 | $this->keys = array_keys($this->ids); |
196 | rsort($this->keys); | ||
197 | $this->position = 0; | 231 | $this->position = 0; |
198 | } | 232 | } |
199 | 233 | ||
@@ -219,6 +253,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
219 | // Create a dummy database for example | 253 | // Create a dummy database for example |
220 | $this->links = array(); | 254 | $this->links = array(); |
221 | $link = array( | 255 | $link = array( |
256 | 'id' => 1, | ||
222 | 'title'=>' Shaarli: the personal, minimalist, super-fast, no-database delicious clone', | 257 | 'title'=>' Shaarli: the personal, minimalist, super-fast, no-database delicious clone', |
223 | 'url'=>'https://github.com/shaarli/Shaarli/wiki', | 258 | 'url'=>'https://github.com/shaarli/Shaarli/wiki', |
224 | 'description'=>'Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login. | 259 | 'description'=>'Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login. |
@@ -227,20 +262,23 @@ To learn how to use Shaarli, consult the link "Help/documentation" at the bottom | |||
227 | 262 | ||
228 | You use the community supported version of the original Shaarli project, by Sebastien Sauvage.', | 263 | You use the community supported version of the original Shaarli project, by Sebastien Sauvage.', |
229 | 'private'=>0, | 264 | 'private'=>0, |
230 | 'linkdate'=> date('Ymd_His'), | 265 | 'created'=> new DateTime(), |
231 | 'tags'=>'opensource software' | 266 | 'tags'=>'opensource software' |
232 | ); | 267 | ); |
233 | $this->links[$link['linkdate']] = $link; | 268 | $link['shorturl'] = link_small_hash($link['created'], $link['id']); |
269 | $this->links[1] = $link; | ||
234 | 270 | ||
235 | $link = array( | 271 | $link = array( |
272 | 'id' => 0, | ||
236 | 'title'=>'My secret stuff... - Pastebin.com', | 273 | 'title'=>'My secret stuff... - Pastebin.com', |
237 | 'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', | 274 | 'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', |
238 | 'description'=>'Shhhh! I\'m a private link only YOU can see. You can delete me too.', | 275 | 'description'=>'Shhhh! I\'m a private link only YOU can see. You can delete me too.', |
239 | 'private'=>1, | 276 | 'private'=>1, |
240 | 'linkdate'=> date('Ymd_His', strtotime('-1 minute')), | 277 | 'created'=> new DateTime('1 minute ago'), |
241 | 'tags'=>'secretstuff' | 278 | 'tags'=>'secretstuff', |
242 | ); | 279 | ); |
243 | $this->links[$link['linkdate']] = $link; | 280 | $link['shorturl'] = link_small_hash($link['created'], $link['id']); |
281 | $this->links[0] = $link; | ||
244 | 282 | ||
245 | // Write database to disk | 283 | // Write database to disk |
246 | $this->write(); | 284 | $this->write(); |
@@ -251,7 +289,6 @@ You use the community supported version of the original Shaarli project, by Seba | |||
251 | */ | 289 | */ |
252 | private function read() | 290 | private function read() |
253 | { | 291 | { |
254 | |||
255 | // Public links are hidden and user not logged in => nothing to show | 292 | // Public links are hidden and user not logged in => nothing to show |
256 | if ($this->hidePublicLinks && !$this->loggedIn) { | 293 | if ($this->hidePublicLinks && !$this->loggedIn) { |
257 | $this->links = array(); | 294 | $this->links = array(); |
@@ -269,23 +306,13 @@ You use the community supported version of the original Shaarli project, by Seba | |||
269 | strlen(self::$phpPrefix), -strlen(self::$phpSuffix))))); | 306 | strlen(self::$phpPrefix), -strlen(self::$phpSuffix))))); |
270 | } | 307 | } |
271 | 308 | ||
272 | // If user is not logged in, filter private links. | 309 | $toremove = array(); |
273 | if (!$this->loggedIn) { | 310 | foreach ($this->links as $key => &$link) { |
274 | $toremove = array(); | 311 | if (! $this->loggedIn && $link['private'] != 0) { |
275 | foreach ($this->links as $link) { | 312 | // Transition for not upgraded databases. |
276 | if ($link['private'] != 0) { | 313 | $toremove[] = $key; |
277 | $toremove[] = $link['linkdate']; | 314 | continue; |
278 | } | ||
279 | } | ||
280 | foreach ($toremove as $linkdate) { | ||
281 | unset($this->links[$linkdate]); | ||
282 | } | 315 | } |
283 | } | ||
284 | |||
285 | $this->urls = array(); | ||
286 | foreach ($this->links as &$link) { | ||
287 | // Keep the list of the mapping URLs-->linkdate up-to-date. | ||
288 | $this->urls[$link['url']] = $link['linkdate']; | ||
289 | 316 | ||
290 | // Sanitize data fields. | 317 | // Sanitize data fields. |
291 | sanitizeLink($link); | 318 | sanitizeLink($link); |
@@ -307,7 +334,24 @@ You use the community supported version of the original Shaarli project, by Seba | |||
307 | else { | 334 | else { |
308 | $link['real_url'] = $link['url']; | 335 | $link['real_url'] = $link['url']; |
309 | } | 336 | } |
337 | |||
338 | // To be able to load links before running the update, and prepare the update | ||
339 | if (! isset($link['created'])) { | ||
340 | $link['id'] = $link['linkdate']; | ||
341 | $link['created'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['linkdate']); | ||
342 | if (! empty($link['updated'])) { | ||
343 | $link['updated'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['updated']); | ||
344 | } | ||
345 | $link['shorturl'] = smallHash($link['linkdate']); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | // If user is not logged in, filter private links. | ||
350 | foreach ($toremove as $offset) { | ||
351 | unset($this->links[$offset]); | ||
310 | } | 352 | } |
353 | |||
354 | $this->reorder(); | ||
311 | } | 355 | } |
312 | 356 | ||
313 | /** | 357 | /** |
@@ -430,7 +474,7 @@ You use the community supported version of the original Shaarli project, by Seba | |||
430 | $request = ''; | 474 | $request = ''; |
431 | } | 475 | } |
432 | 476 | ||
433 | $linkFilter = new LinkFilter($this->links); | 477 | $linkFilter = new LinkFilter($this); |
434 | return $linkFilter->filter($type, $request, $casesensitive, $privateonly); | 478 | return $linkFilter->filter($type, $request, $casesensitive, $privateonly); |
435 | } | 479 | } |
436 | 480 | ||
@@ -467,12 +511,64 @@ You use the community supported version of the original Shaarli project, by Seba | |||
467 | public function days() | 511 | public function days() |
468 | { | 512 | { |
469 | $linkDays = array(); | 513 | $linkDays = array(); |
470 | foreach (array_keys($this->links) as $day) { | 514 | foreach ($this->links as $link) { |
471 | $linkDays[substr($day, 0, 8)] = 0; | 515 | $linkDays[$link['created']->format('Ymd')] = 0; |
472 | } | 516 | } |
473 | $linkDays = array_keys($linkDays); | 517 | $linkDays = array_keys($linkDays); |
474 | sort($linkDays); | 518 | sort($linkDays); |
475 | 519 | ||
476 | return $linkDays; | 520 | return $linkDays; |
477 | } | 521 | } |
522 | |||
523 | /** | ||
524 | * Reorder links by creation date (newest first). | ||
525 | * | ||
526 | * Also update the urls and ids mapping arrays. | ||
527 | * | ||
528 | * @param string $order ASC|DESC | ||
529 | */ | ||
530 | public function reorder($order = 'DESC') | ||
531 | { | ||
532 | $order = $order === 'ASC' ? -1 : 1; | ||
533 | // Reorder array by dates. | ||
534 | usort($this->links, function($a, $b) use ($order) { | ||
535 | return $a['created'] < $b['created'] ? 1 * $order : -1 * $order; | ||
536 | }); | ||
537 | |||
538 | $this->urls = array(); | ||
539 | $this->ids = array(); | ||
540 | foreach ($this->links as $key => $link) { | ||
541 | $this->urls[$link['url']] = $key; | ||
542 | $this->ids[$link['id']] = $key; | ||
543 | } | ||
544 | } | ||
545 | |||
546 | /** | ||
547 | * Return the next key for link creation. | ||
548 | * E.g. If the last ID is 597, the next will be 598. | ||
549 | * | ||
550 | * @return int next ID. | ||
551 | */ | ||
552 | public function getNextId() | ||
553 | { | ||
554 | if (!empty($this->ids)) { | ||
555 | return max(array_keys($this->ids)) + 1; | ||
556 | } | ||
557 | return 0; | ||
558 | } | ||
559 | |||
560 | /** | ||
561 | * Returns a link offset in links array from its unique ID. | ||
562 | * | ||
563 | * @param int $id Persistent ID of a link. | ||
564 | * | ||
565 | * @return int Real offset in local array, or null if doesn't exist. | ||
566 | */ | ||
567 | protected function getLinkOffset($id) | ||
568 | { | ||
569 | if (isset($this->ids[$id])) { | ||
570 | return $this->ids[$id]; | ||
571 | } | ||
572 | return null; | ||
573 | } | ||
478 | } | 574 | } |
diff --git a/application/LinkFilter.php b/application/LinkFilter.php index d4fe28df..daa6d9cc 100644 --- a/application/LinkFilter.php +++ b/application/LinkFilter.php | |||
@@ -33,12 +33,12 @@ class LinkFilter | |||
33 | public static $HASHTAG_CHARS = '\p{Pc}\p{N}\p{L}\p{Mn}'; | 33 | public static $HASHTAG_CHARS = '\p{Pc}\p{N}\p{L}\p{Mn}'; |
34 | 34 | ||
35 | /** | 35 | /** |
36 | * @var array all available links. | 36 | * @var LinkDB all available links. |
37 | */ | 37 | */ |
38 | private $links; | 38 | private $links; |
39 | 39 | ||
40 | /** | 40 | /** |
41 | * @param array $links initialization. | 41 | * @param LinkDB $links initialization. |
42 | */ | 42 | */ |
43 | public function __construct($links) | 43 | public function __construct($links) |
44 | { | 44 | { |
@@ -94,18 +94,16 @@ class LinkFilter | |||
94 | private function noFilter($privateonly = false) | 94 | private function noFilter($privateonly = false) |
95 | { | 95 | { |
96 | if (! $privateonly) { | 96 | if (! $privateonly) { |
97 | krsort($this->links); | ||
98 | return $this->links; | 97 | return $this->links; |
99 | } | 98 | } |
100 | 99 | ||
101 | $out = array(); | 100 | $out = array(); |
102 | foreach ($this->links as $value) { | 101 | foreach ($this->links as $key => $value) { |
103 | if ($value['private']) { | 102 | if ($value['private']) { |
104 | $out[$value['linkdate']] = $value; | 103 | $out[$key] = $value; |
105 | } | 104 | } |
106 | } | 105 | } |
107 | 106 | ||
108 | krsort($out); | ||
109 | return $out; | 107 | return $out; |
110 | } | 108 | } |
111 | 109 | ||
@@ -121,10 +119,10 @@ class LinkFilter | |||
121 | private function filterSmallHash($smallHash) | 119 | private function filterSmallHash($smallHash) |
122 | { | 120 | { |
123 | $filtered = array(); | 121 | $filtered = array(); |
124 | foreach ($this->links as $l) { | 122 | foreach ($this->links as $key => $l) { |
125 | if ($smallHash == smallHash($l['linkdate'])) { | 123 | if ($smallHash == $l['shorturl']) { |
126 | // Yes, this is ugly and slow | 124 | // Yes, this is ugly and slow |
127 | $filtered[$l['linkdate']] = $l; | 125 | $filtered[$key] = $l; |
128 | return $filtered; | 126 | return $filtered; |
129 | } | 127 | } |
130 | } | 128 | } |
@@ -188,7 +186,7 @@ class LinkFilter | |||
188 | $keys = array('title', 'description', 'url', 'tags'); | 186 | $keys = array('title', 'description', 'url', 'tags'); |
189 | 187 | ||
190 | // Iterate over every stored link. | 188 | // Iterate over every stored link. |
191 | foreach ($this->links as $link) { | 189 | foreach ($this->links as $id => $link) { |
192 | 190 | ||
193 | // ignore non private links when 'privatonly' is on. | 191 | // ignore non private links when 'privatonly' is on. |
194 | if (! $link['private'] && $privateonly === true) { | 192 | if (! $link['private'] && $privateonly === true) { |
@@ -222,11 +220,10 @@ class LinkFilter | |||
222 | } | 220 | } |
223 | 221 | ||
224 | if ($found) { | 222 | if ($found) { |
225 | $filtered[$link['linkdate']] = $link; | 223 | $filtered[$id] = $link; |
226 | } | 224 | } |
227 | } | 225 | } |
228 | 226 | ||
229 | krsort($filtered); | ||
230 | return $filtered; | 227 | return $filtered; |
231 | } | 228 | } |
232 | 229 | ||
@@ -256,7 +253,7 @@ class LinkFilter | |||
256 | return $filtered; | 253 | return $filtered; |
257 | } | 254 | } |
258 | 255 | ||
259 | foreach ($this->links as $link) { | 256 | foreach ($this->links as $key => $link) { |
260 | // ignore non private links when 'privatonly' is on. | 257 | // ignore non private links when 'privatonly' is on. |
261 | if (! $link['private'] && $privateonly === true) { | 258 | if (! $link['private'] && $privateonly === true) { |
262 | continue; | 259 | continue; |
@@ -278,10 +275,9 @@ class LinkFilter | |||
278 | } | 275 | } |
279 | 276 | ||
280 | if ($found) { | 277 | if ($found) { |
281 | $filtered[$link['linkdate']] = $link; | 278 | $filtered[$key] = $link; |
282 | } | 279 | } |
283 | } | 280 | } |
284 | krsort($filtered); | ||
285 | return $filtered; | 281 | return $filtered; |
286 | } | 282 | } |
287 | 283 | ||
@@ -304,13 +300,14 @@ class LinkFilter | |||
304 | } | 300 | } |
305 | 301 | ||
306 | $filtered = array(); | 302 | $filtered = array(); |
307 | foreach ($this->links as $l) { | 303 | foreach ($this->links as $key => $l) { |
308 | if (startsWith($l['linkdate'], $day)) { | 304 | if ($l['created']->format('Ymd') == $day) { |
309 | $filtered[$l['linkdate']] = $l; | 305 | $filtered[$key] = $l; |
310 | } | 306 | } |
311 | } | 307 | } |
312 | ksort($filtered); | 308 | |
313 | return $filtered; | 309 | // sort by date ASC |
310 | return array_reverse($filtered, true); | ||
314 | } | 311 | } |
315 | 312 | ||
316 | /** | 313 | /** |
diff --git a/application/LinkUtils.php b/application/LinkUtils.php index 9d9ae3cb..cf58f808 100644 --- a/application/LinkUtils.php +++ b/application/LinkUtils.php | |||
@@ -169,3 +169,16 @@ function space2nbsp($text) | |||
169 | function format_description($description, $redirector = '', $indexUrl = '') { | 169 | function format_description($description, $redirector = '', $indexUrl = '') { |
170 | return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector), $indexUrl))); | 170 | return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector), $indexUrl))); |
171 | } | 171 | } |
172 | |||
173 | /** | ||
174 | * Generate a small hash for a link. | ||
175 | * | ||
176 | * @param DateTime $date Link creation date. | ||
177 | * @param int $id Link ID. | ||
178 | * | ||
179 | * @return string the small hash generated from link data. | ||
180 | */ | ||
181 | function link_small_hash($date, $id) | ||
182 | { | ||
183 | return smallHash($date->format(LinkDB::LINK_DATE_FORMAT) . $id); | ||
184 | } | ||
diff --git a/application/NetscapeBookmarkUtils.php b/application/NetscapeBookmarkUtils.php index dd21f05b..f21ee359 100644 --- a/application/NetscapeBookmarkUtils.php +++ b/application/NetscapeBookmarkUtils.php | |||
@@ -38,7 +38,7 @@ class NetscapeBookmarkUtils | |||
38 | if ($link['private'] == 0 && $selection == 'private') { | 38 | if ($link['private'] == 0 && $selection == 'private') { |
39 | continue; | 39 | continue; |
40 | } | 40 | } |
41 | $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']); | 41 | $date = $link['created']; |
42 | $link['timestamp'] = $date->getTimestamp(); | 42 | $link['timestamp'] = $date->getTimestamp(); |
43 | $link['taglist'] = str_replace(' ', ',', $link['tags']); | 43 | $link['taglist'] = str_replace(' ', ',', $link['tags']); |
44 | 44 | ||
@@ -147,6 +147,143 @@ class NetscapeBookmarkUtils | |||
147 | 'url' => $bkm['uri'], | 147 | 'url' => $bkm['uri'], |
148 | 'description' => $bkm['note'], | 148 | 'description' => $bkm['note'], |
149 | 'private' => $private, | 149 | 'private' => $private, |
150 | 'tags' => $bkm['tags'] | ||
151 | ); | ||
152 | |||
153 | $existingLink = $linkDb->getLinkFromUrl($bkm['uri']); | ||
154 | |||
155 | if ($existingLink !== false) { | ||
156 | if ($overwrite === false) { | ||
157 | // Do not overwrite an existing link | ||
158 | $skipCount++; | ||
159 | continue; | ||
160 | } | ||
161 | |||
162 | // Overwrite an existing link, keep its date | ||
163 | $newLink['id'] = $existingLink['id']; | ||
164 | $newLink['created'] = $existingLink['created']; | ||
165 | $newLink['updated'] = new DateTime(); | ||
166 | $linkDb[$existingLink['id']] = $newLink; | ||
167 | $importCount++; | ||
168 | $overwriteCount++; | ||
169 | continue; | ||
170 | } | ||
171 | |||
172 | // Add a new link - @ used for UNIX timestamps | ||
173 | $newLinkDate = new DateTime('@'.strval($bkm['time'])); | ||
174 | $newLinkDate->setTimezone(new DateTimeZone(date_default_timezone_get())); | ||
175 | $newLink['created'] = $newLinkDate; | ||
176 | $newLink['id'] = $linkDb->getNextId(); | ||
177 | $newLink['shorturl'] = link_small_hash($newLink['created'], $newLink['id']); | ||
178 | $linkDb[$newLink['id']] = $newLink; | ||
179 | $importCount++; | ||
180 | } | ||
181 | |||
182 | $linkDb->save($pagecache); | ||
183 | return self::importStatus( | ||
184 | $filename, | ||
185 | $filesize, | ||
186 | $importCount, | ||
187 | $overwriteCount, | ||
188 | $skipCount | ||
189 | ); | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * Generates an import status summary | ||
194 | * | ||
195 | * @param string $filename name of the file to import | ||
196 | * @param int $filesize size of the file to import | ||
197 | * @param int $importCount how many links were imported | ||
198 | * @param int $overwriteCount how many links were overwritten | ||
199 | * @param int $skipCount how many links were skipped | ||
200 | * | ||
201 | * @return string Summary of the bookmark import status | ||
202 | */ | ||
203 | private static function importStatus( | ||
204 | $filename, | ||
205 | $filesize, | ||
206 | $importCount=0, | ||
207 | $overwriteCount=0, | ||
208 | $skipCount=0 | ||
209 | ) | ||
210 | { | ||
211 | $status = 'File '.$filename.' ('.$filesize.' bytes) '; | ||
212 | if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) { | ||
213 | $status .= 'has an unknown file format. Nothing was imported.'; | ||
214 | } else { | ||
215 | $status .= 'was successfully processed: '.$importCount.' links imported, '; | ||
216 | $status .= $overwriteCount.' links overwritten, '; | ||
217 | $status .= $skipCount.' links skipped.'; | ||
218 | } | ||
219 | return $status; | ||
220 | } | ||
221 | |||
222 | /** | ||
223 | * Imports Web bookmarks from an uploaded Netscape bookmark dump | ||
224 | * | ||
225 | * @param array $post Server $_POST parameters | ||
226 | * @param array $files Server $_FILES parameters | ||
227 | * @param LinkDB $linkDb Loaded LinkDB instance | ||
228 | * @param string $pagecache Page cache | ||
229 | * | ||
230 | * @return string Summary of the bookmark import status | ||
231 | */ | ||
232 | public static function import($post, $files, $linkDb, $pagecache) | ||
233 | { | ||
234 | $filename = $files['filetoupload']['name']; | ||
235 | $filesize = $files['filetoupload']['size']; | ||
236 | $data = file_get_contents($files['filetoupload']['tmp_name']); | ||
237 | |||
238 | if (strpos($data, '<!DOCTYPE NETSCAPE-Bookmark-file-1>') === false) { | ||
239 | return self::importStatus($filename, $filesize); | ||
240 | } | ||
241 | |||
242 | // Overwrite existing links? | ||
243 | $overwrite = ! empty($post['overwrite']); | ||
244 | |||
245 | // Add tags to all imported links? | ||
246 | if (empty($post['default_tags'])) { | ||
247 | $defaultTags = array(); | ||
248 | } else { | ||
249 | $defaultTags = preg_split( | ||
250 | '/[\s,]+/', | ||
251 | escape($post['default_tags']) | ||
252 | ); | ||
253 | } | ||
254 | |||
255 | // links are imported as public by default | ||
256 | $defaultPrivacy = 0; | ||
257 | |||
258 | $parser = new NetscapeBookmarkParser( | ||
259 | true, // nested tag support | ||
260 | $defaultTags, // additional user-specified tags | ||
261 | strval(1 - $defaultPrivacy) // defaultPub = 1 - defaultPrivacy | ||
262 | ); | ||
263 | $bookmarks = $parser->parseString($data); | ||
264 | |||
265 | $importCount = 0; | ||
266 | $overwriteCount = 0; | ||
267 | $skipCount = 0; | ||
268 | |||
269 | foreach ($bookmarks as $bkm) { | ||
270 | $private = $defaultPrivacy; | ||
271 | if (empty($post['privacy']) || $post['privacy'] == 'default') { | ||
272 | // use value from the imported file | ||
273 | $private = $bkm['pub'] == '1' ? 0 : 1; | ||
274 | } else if ($post['privacy'] == 'private') { | ||
275 | // all imported links are private | ||
276 | $private = 1; | ||
277 | } else if ($post['privacy'] == 'public') { | ||
278 | // all imported links are public | ||
279 | $private = 0; | ||
280 | } | ||
281 | |||
282 | $newLink = array( | ||
283 | 'title' => $bkm['title'], | ||
284 | 'url' => $bkm['uri'], | ||
285 | 'description' => $bkm['note'], | ||
286 | 'private' => $private, | ||
150 | 'linkdate'=> '', | 287 | 'linkdate'=> '', |
151 | 'tags' => $bkm['tags'] | 288 | 'tags' => $bkm['tags'] |
152 | ); | 289 | ); |
diff --git a/application/Updater.php b/application/Updater.php index 36eddd4f..f0d02814 100644 --- a/application/Updater.php +++ b/application/Updater.php | |||
@@ -138,10 +138,10 @@ class Updater | |||
138 | public function updateMethodRenameDashTags() | 138 | public function updateMethodRenameDashTags() |
139 | { | 139 | { |
140 | $linklist = $this->linkDB->filterSearch(); | 140 | $linklist = $this->linkDB->filterSearch(); |
141 | foreach ($linklist as $link) { | 141 | foreach ($linklist as $key => $link) { |
142 | $link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']); | 142 | $link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']); |
143 | $link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true))); | 143 | $link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true))); |
144 | $this->linkDB[$link['linkdate']] = $link; | 144 | $this->linkDB[$key] = $link; |
145 | } | 145 | } |
146 | $this->linkDB->save($this->conf->get('resource.page_cache')); | 146 | $this->linkDB->save($this->conf->get('resource.page_cache')); |
147 | return true; | 147 | return true; |
@@ -215,6 +215,47 @@ class Updater | |||
215 | } | 215 | } |
216 | return true; | 216 | return true; |
217 | } | 217 | } |
218 | |||
219 | /** | ||
220 | * Update the database to use the new ID system, which replaces linkdate primary keys. | ||
221 | * Also, creation and update dates are now DateTime objects (done by LinkDB). | ||
222 | * | ||
223 | * Since this update is very sensitve (changing the whole database), the datastore will be | ||
224 | * automatically backed up into the file datastore.<datetime>.php. | ||
225 | * | ||
226 | * LinkDB also adds the field 'shorturl' with the precedent format (linkdate smallhash), | ||
227 | * which will be saved by this method. | ||
228 | * | ||
229 | * @return bool true if the update is successful, false otherwise. | ||
230 | */ | ||
231 | public function updateMethodDatastoreIds() | ||
232 | { | ||
233 | // up to date database | ||
234 | if (isset($this->linkDB[0])) { | ||
235 | return true; | ||
236 | } | ||
237 | |||
238 | $save = $this->conf->get('resource.data_dir') .'/datastore.'. date('YmdHis') .'.php'; | ||
239 | copy($this->conf->get('resource.datastore'), $save); | ||
240 | |||
241 | $links = array(); | ||
242 | foreach ($this->linkDB as $offset => $value) { | ||
243 | $links[] = $value; | ||
244 | unset($this->linkDB[$offset]); | ||
245 | } | ||
246 | $links = array_reverse($links); | ||
247 | $cpt = 0; | ||
248 | foreach ($links as $l) { | ||
249 | unset($l['linkdate']); | ||
250 | $l['id'] = $cpt; | ||
251 | $this->linkDB[$cpt++] = $l; | ||
252 | } | ||
253 | |||
254 | $this->linkDB->save($this->conf->get('resource.page_cache')); | ||
255 | $this->linkDB->reorder(); | ||
256 | |||
257 | return true; | ||
258 | } | ||
218 | } | 259 | } |
219 | 260 | ||
220 | /** | 261 | /** |
diff --git a/application/Utils.php b/application/Utils.php index 0166ee2a..0a5b476e 100644 --- a/application/Utils.php +++ b/application/Utils.php | |||
@@ -31,7 +31,11 @@ function logm($logFile, $clientIp, $message) | |||
31 | * - are NOT cryptographically secure (they CAN be forged) | 31 | * - are NOT cryptographically secure (they CAN be forged) |
32 | * | 32 | * |
33 | * In Shaarli, they are used as a tinyurl-like link to individual entries, | 33 | * In Shaarli, they are used as a tinyurl-like link to individual entries, |
34 | * e.g. smallHash('20111006_131924') --> yZH23w | 34 | * built once with the combination of the date and item ID. |
35 | * e.g. smallHash('20111006_131924' . 142) --> eaWxtQ | ||
36 | * | ||
37 | * @warning before v0.8.1, smallhashes were built only with the date, | ||
38 | * and their value has been preserved. | ||
35 | * | 39 | * |
36 | * @param string $text Create a hash from this text. | 40 | * @param string $text Create a hash from this text. |
37 | * | 41 | * |
diff --git a/cache/.htaccess b/cache/.htaccess index b584d98c..f601c1ee 100644 --- a/cache/.htaccess +++ b/cache/.htaccess | |||
@@ -1,2 +1,13 @@ | |||
1 | Allow from none | 1 | <IfModule version_module> |
2 | Deny from all | 2 | <IfVersion >= 2.4> |
3 | Require all denied | ||
4 | </IfVersion> | ||
5 | <IfVersion < 2.4> | ||
6 | Allow from none | ||
7 | Deny from all | ||
8 | </IfVersion> | ||
9 | </IfModule> | ||
10 | |||
11 | <IfModule !version_module> | ||
12 | Require all denied | ||
13 | </IfModule> | ||
diff --git a/data/.htaccess b/data/.htaccess index b584d98c..f601c1ee 100644 --- a/data/.htaccess +++ b/data/.htaccess | |||
@@ -1,2 +1,13 @@ | |||
1 | Allow from none | 1 | <IfModule version_module> |
2 | Deny from all | 2 | <IfVersion >= 2.4> |
3 | Require all denied | ||
4 | </IfVersion> | ||
5 | <IfVersion < 2.4> | ||
6 | Allow from none | ||
7 | Deny from all | ||
8 | </IfVersion> | ||
9 | </IfModule> | ||
10 | |||
11 | <IfModule !version_module> | ||
12 | Require all denied | ||
13 | </IfModule> | ||
diff --git a/doc/Community-&-Related-software.html b/doc/Community-&-Related-software.html index accbacdc..cbc73d54 100644 --- a/doc/Community-&-Related-software.html +++ b/doc/Community-&-Related-software.html | |||
@@ -81,10 +81,11 @@ | |||
81 | <ul> | 81 | <ul> |
82 | <li><a href="https://github.com/kalvn/shaarli-plugin-autosave">autosave</a> by <a href="https://github.com/kalvn">@kalvn</a>: Automatically saves data when editing a link to avoid any loss in case of crash or unexpected shutdown.<a href=".html"></a></li> | 82 | <li><a href="https://github.com/kalvn/shaarli-plugin-autosave">autosave</a> by <a href="https://github.com/kalvn">@kalvn</a>: Automatically saves data when editing a link to avoid any loss in case of crash or unexpected shutdown.<a href=".html"></a></li> |
83 | <li><a href="https://github.com/ArthurHoaro/code-coloration">Code Coloration</a> by <a href="https://github.com/ArthurHoaro">@ArthurHoaro</a>: client side code syntax highlighter.<a href=".html"></a></li> | 83 | <li><a href="https://github.com/ArthurHoaro/code-coloration">Code Coloration</a> by <a href="https://github.com/ArthurHoaro">@ArthurHoaro</a>: client side code syntax highlighter.<a href=".html"></a></li> |
84 | <li><a href="https://github.com/alexisju/social">social</a> by <a href="https://github.com/alexisju">@alexisju</a>: share links to social networks.<a href=".html"></a></li> | 84 | <li><a href="https://github.com/kalvn/shaarli-plugin-disqus">Disqus</a> by <a href="https://github.com/kalvn">@kalvn</a>: Adds Disqus comment system to your Shaarli.<a href=".html"></a></li> |
85 | <li><a href="https://github.com/NerosTie/emojione">emojione</a> by <a href="https://github.com/NerosTie">@NerosTie</a>: Add colorful emojis to your Shaarli.<a href=".html"></a></li> | 85 | <li><a href="https://github.com/NerosTie/emojione">emojione</a> by <a href="https://github.com/NerosTie">@NerosTie</a>: Add colorful emojis to your Shaarli.<a href=".html"></a></li> |
86 | <li><a href="https://github.com/ArthurHoaro/launch-plugin">launch</a> - Launch Plugin is a plugin designed to enhance and customize Launch Theme for Shaarli.<a href=".html"></a></li> | 86 | <li><a href="https://github.com/ArthurHoaro/launch-plugin">launch</a> - Launch Plugin is a plugin designed to enhance and customize Launch Theme for Shaarli.<a href=".html"></a></li> |
87 | <li><a href="https://github.com/kalvn/shaarli-plugin-disqus">Disqus</a> by <a href="https://github.com/kalvn">@kalvn</a>: Adds Disqus comment system to your Shaarli.<a href=".html"></a></li> | 87 | <li><a href="https://github.com/alexisju/social">social</a> by <a href="https://github.com/alexisju">@alexisju</a>: share links to social networks.<a href=".html"></a></li> |
88 | <li><a href="https://github.com/ArthurHoaro/shaarli2twitter">shaarli2twitter</a> by <a href="https://github.com/ArthurHoaro">@ArthurHoaro</a> - Automatically tweet your shared links from Shaarli<a href=".html"></a></li> | ||
88 | </ul> | 89 | </ul> |
89 | <h3 id="themes">Themes</h3> | 90 | <h3 id="themes">Themes</h3> |
90 | <p>See <a href="Theming.html">Theming</a> for the list of community-contributed themes, and an installation guide.</p> | 91 | <p>See <a href="Theming.html">Theming</a> for the list of community-contributed themes, and an installation guide.</p> |
@@ -95,7 +96,7 @@ | |||
95 | <li><a href="https://github.com/DMeloni/shaarlo">Shaarlo</a> - An aggregator for shaarlis with many features (a very popular running instance among french shaarliers: <a href="http://shaarli.fr/">shaarli.fr</a>)<a href=".html"></a></li> | 96 | <li><a href="https://github.com/DMeloni/shaarlo">Shaarlo</a> - An aggregator for shaarlis with many features (a very popular running instance among french shaarliers: <a href="http://shaarli.fr/">shaarli.fr</a>)<a href=".html"></a></li> |
96 | <li><a href="https://github.com/BoboTiG/shaarlimages">Shaarlimages</a> - An image-oriented aggregator for Shaarlis<a href=".html"></a></li> | 97 | <li><a href="https://github.com/BoboTiG/shaarlimages">Shaarlimages</a> - An image-oriented aggregator for Shaarlis<a href=".html"></a></li> |
97 | <li><a href="https://github.com/mknexen/shaarli-api">mknexen/shaarli-api</a> - A REST API for Shaarli<a href=".html"></a></li> | 98 | <li><a href="https://github.com/mknexen/shaarli-api">mknexen/shaarli-api</a> - A REST API for Shaarli<a href=".html"></a></li> |
98 | <li><a href="https://github.com/qwertygc/shaarli-dev-code/blob/master/self-dead-link.php">Self dead link</a> - Detect dead links on shaarli. This version use the database of shaarli. An <a href="https://github.com/qwertygc/shaarli-dev-code/blob/master/dead-link.php">another version</a>, can be used for others shaarli (but use most ressources).<a href=".html"></a></li> | 99 | <li><a href="https://github.com/qwertygc/shaarli-dev-code/blob/master/self-dead-link.php">Self dead link</a> - Detect dead links on shaarli. This version use the database of shaarli. <a href="https://github.com/qwertygc/shaarli-dev-code/blob/master/dead-link.php">Another version</a>, can be used for other shaarli instances (but is more resource consuming).<a href=".html"></a></li> |
99 | </ul> | 100 | </ul> |
100 | <h3 id="mobile-apps">Mobile Apps</h3> | 101 | <h3 id="mobile-apps">Mobile Apps</h3> |
101 | <ul> | 102 | <ul> |
@@ -107,6 +108,7 @@ | |||
107 | <ul> | 108 | <ul> |
108 | <li><a href="https://github.com/jcsaaddupuy/tt-rss-shaarli">tt-rss-shaarli</a> - <a href="http://tt-rss.org/">TinyTiny RSS</a> plugin that adds support for sharing articles with Shaarli<a href=".html"></a></li> | 109 | <li><a href="https://github.com/jcsaaddupuy/tt-rss-shaarli">tt-rss-shaarli</a> - <a href="http://tt-rss.org/">TinyTiny RSS</a> plugin that adds support for sharing articles with Shaarli<a href=".html"></a></li> |
109 | <li><a href="https://github.com/ahmet2mir/octopress-shaarli">octopress-shaarli</a> - Octopress plugin to retrieve Shaarli links on the sidebar<a href=".html"></a></li> | 110 | <li><a href="https://github.com/ahmet2mir/octopress-shaarli">octopress-shaarli</a> - Octopress plugin to retrieve Shaarli links on the sidebar<a href=".html"></a></li> |
111 | <li><a href="https://github.com/q2apro/scuttle-to-shaarli">Scuttle to Shaarli</a> - Import bookmarks from Scuttle<a href=".html"></a></li> | ||
110 | </ul> | 112 | </ul> |
111 | <h2 id="alternatives-to-shaarli">Alternatives to Shaarli</h2> | 113 | <h2 id="alternatives-to-shaarli">Alternatives to Shaarli</h2> |
112 | <ul> | 114 | <ul> |
diff --git a/doc/Community-&-Related-software.md b/doc/Community-&-Related-software.md index 3945d005..291bf643 100644 --- a/doc/Community-&-Related-software.md +++ b/doc/Community-&-Related-software.md | |||
@@ -20,10 +20,11 @@ _TODO: contact repos owners to see if they'd like to standardize their work with | |||
20 | 20 | ||
21 | * [autosave](https://github.com/kalvn/shaarli-plugin-autosave) by [@kalvn](https://github.com/kalvn): Automatically saves data when editing a link to avoid any loss in case of crash or unexpected shutdown.[](.html) | 21 | * [autosave](https://github.com/kalvn/shaarli-plugin-autosave) by [@kalvn](https://github.com/kalvn): Automatically saves data when editing a link to avoid any loss in case of crash or unexpected shutdown.[](.html) |
22 | * [Code Coloration](https://github.com/ArthurHoaro/code-coloration) by [@ArthurHoaro](https://github.com/ArthurHoaro): client side code syntax highlighter.[](.html) | 22 | * [Code Coloration](https://github.com/ArthurHoaro/code-coloration) by [@ArthurHoaro](https://github.com/ArthurHoaro): client side code syntax highlighter.[](.html) |
23 | * [social](https://github.com/alexisju/social) by [@alexisju](https://github.com/alexisju): share links to social networks.[](.html) | 23 | * [Disqus](https://github.com/kalvn/shaarli-plugin-disqus) by [@kalvn](https://github.com/kalvn): Adds Disqus comment system to your Shaarli.[](.html) |
24 | * [emojione](https://github.com/NerosTie/emojione) by [@NerosTie](https://github.com/NerosTie): Add colorful emojis to your Shaarli.[](.html) | 24 | * [emojione](https://github.com/NerosTie/emojione) by [@NerosTie](https://github.com/NerosTie): Add colorful emojis to your Shaarli.[](.html) |
25 | * [launch](https://github.com/ArthurHoaro/launch-plugin) - Launch Plugin is a plugin designed to enhance and customize Launch Theme for Shaarli.[](.html) | 25 | * [launch](https://github.com/ArthurHoaro/launch-plugin) - Launch Plugin is a plugin designed to enhance and customize Launch Theme for Shaarli.[](.html) |
26 | * [Disqus](https://github.com/kalvn/shaarli-plugin-disqus) by [@kalvn](https://github.com/kalvn): Adds Disqus comment system to your Shaarli.[](.html) | 26 | * [social](https://github.com/alexisju/social) by [@alexisju](https://github.com/alexisju): share links to social networks.[](.html) |
27 | * [shaarli2twitter](https://github.com/ArthurHoaro/shaarli2twitter) by [@ArthurHoaro](https://github.com/ArthurHoaro) - Automatically tweet your shared links from Shaarli[](.html) | ||
27 | 28 | ||
28 | 29 | ||
29 | ### Themes | 30 | ### Themes |
@@ -35,7 +36,7 @@ See [Theming](Theming.html) for the list of community-contributed themes, and an | |||
35 | - [Shaarlo](https://github.com/DMeloni/shaarlo) - An aggregator for shaarlis with many features (a very popular running instance among french shaarliers: [shaarli.fr](http://shaarli.fr/))[](.html) | 36 | - [Shaarlo](https://github.com/DMeloni/shaarlo) - An aggregator for shaarlis with many features (a very popular running instance among french shaarliers: [shaarli.fr](http://shaarli.fr/))[](.html) |
36 | - [Shaarlimages](https://github.com/BoboTiG/shaarlimages) - An image-oriented aggregator for Shaarlis[](.html) | 37 | - [Shaarlimages](https://github.com/BoboTiG/shaarlimages) - An image-oriented aggregator for Shaarlis[](.html) |
37 | - [mknexen/shaarli-api](https://github.com/mknexen/shaarli-api) - A REST API for Shaarli[](.html) | 38 | - [mknexen/shaarli-api](https://github.com/mknexen/shaarli-api) - A REST API for Shaarli[](.html) |
38 | - [Self dead link](https://github.com/qwertygc/shaarli-dev-code/blob/master/self-dead-link.php) - Detect dead links on shaarli. This version use the database of shaarli. An [another version](https://github.com/qwertygc/shaarli-dev-code/blob/master/dead-link.php), can be used for others shaarli (but use most ressources).[](.html) | 39 | - [Self dead link](https://github.com/qwertygc/shaarli-dev-code/blob/master/self-dead-link.php) - Detect dead links on shaarli. This version use the database of shaarli. [Another version](https://github.com/qwertygc/shaarli-dev-code/blob/master/dead-link.php), can be used for other shaarli instances (but is more resource consuming).[](.html) |
39 | 40 | ||
40 | ### Mobile Apps | 41 | ### Mobile Apps |
41 | - [Shaarli💫](http://app.mro.name/Shaarli💫) iOS share extension - see [#308](https://github.com/shaarli/Shaarli/issues/308#issuecomment-184592070) for some promo codes,[](.html) | 42 | - [Shaarli💫](http://app.mro.name/Shaarli💫) iOS share extension - see [#308](https://github.com/shaarli/Shaarli/issues/308#issuecomment-184592070) for some promo codes,[](.html) |
@@ -45,6 +46,7 @@ See [Theming](Theming.html) for the list of community-contributed themes, and an | |||
45 | ## Integration with other platforms | 46 | ## Integration with other platforms |
46 | - [tt-rss-shaarli](https://github.com/jcsaaddupuy/tt-rss-shaarli) - [TinyTiny RSS](http://tt-rss.org/) plugin that adds support for sharing articles with Shaarli[](.html) | 47 | - [tt-rss-shaarli](https://github.com/jcsaaddupuy/tt-rss-shaarli) - [TinyTiny RSS](http://tt-rss.org/) plugin that adds support for sharing articles with Shaarli[](.html) |
47 | - [octopress-shaarli](https://github.com/ahmet2mir/octopress-shaarli) - Octopress plugin to retrieve Shaarli links on the sidebar[](.html) | 48 | - [octopress-shaarli](https://github.com/ahmet2mir/octopress-shaarli) - Octopress plugin to retrieve Shaarli links on the sidebar[](.html) |
49 | - [Scuttle to Shaarli](https://github.com/q2apro/scuttle-to-shaarli) - Import bookmarks from Scuttle[](.html) | ||
48 | 50 | ||
49 | ## Alternatives to Shaarli | 51 | ## Alternatives to Shaarli |
50 | - [Shaarli alternatives](http://alternativeto.net/software/shaarli/) (alternativeto.net)[](.html) | 52 | - [Shaarli alternatives](http://alternativeto.net/software/shaarli/) (alternativeto.net)[](.html) |
diff --git a/doc/Download-and-Installation.html b/doc/Download-and-Installation.html index 17c7b69e..b9cac360 100644 --- a/doc/Download-and-Installation.html +++ b/doc/Download-and-Installation.html | |||
@@ -105,13 +105,14 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
105 | <p>Several releases are available:</p> | 105 | <p>Several releases are available:</p> |
106 | <hr /> | 106 | <hr /> |
107 | <h2 id="latest-release-recommended">Latest release (recommended)</h2> | 107 | <h2 id="latest-release-recommended">Latest release (recommended)</h2> |
108 | <p>Get the latest released version from the <a href="https://github.com/shaarli/Shaarli/releases">releases</a> page.<a href=".html"></a></p> | ||
109 | <p>The current latest released version is <code>v0.7.0</code>.</p> | ||
110 | <h3 id="download-as-an-archive">Download as an archive</h3> | 108 | <h3 id="download-as-an-archive">Download as an archive</h3> |
111 | <p>As a .zip archive:</p> | 109 | <p>Get the latest released version from the <a href="https://github.com/shaarli/Shaarli/releases">releases</a> page.<a href=".html"></a></p> |
112 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">wget</span> https://github.com/shaarli/Shaarli/archive/v0.7.0.zip | 110 | <p><strong>Download our <em>shaarli-full</em> archive</strong> to include dependencies.</p> |
113 | $ <span class="fu">unzip</span> Shaarli-v0.7.0.zip | 111 | <p>The current latest released version is <code>v0.8.0</code></p> |
114 | $ <span class="fu">mv</span> Shaarli-v0.7.0 /path/to/shaarli/</code></pre></div> | 112 | <p>Or in command lines:</p> |
113 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">wget</span> https://github.com/shaarli/Shaarli/releases/download/v0.8.0/shaarli-v0.8.0-full.zip | ||
114 | $ <span class="fu">unzip</span> shaarli-v0.8.0-full.zip | ||
115 | $ <span class="fu">mv</span> Shaarli /path/to/shaarli/</code></pre></div> | ||
115 | <table style="width:46%;"> | 116 | <table style="width:46%;"> |
116 | <colgroup> | 117 | <colgroup> |
117 | <col style="width: 8%" /> | 118 | <col style="width: 8%" /> |
@@ -126,6 +127,10 @@ $ <span class="fu">mv</span> Shaarli-v0.7.0 /path/to/shaarli/</code></pre></div> | |||
126 | <tbody> | 127 | <tbody> |
127 | </tbody> | 128 | </tbody> |
128 | </table> | 129 | </table> |
130 | <h3 id="using-git">Using git</h3> | ||
131 | <pre><code>mkdir -p /path/to/shaarli && cd /path/to/shaarli/ | ||
132 | git clone -b v0.8.0 https://github.com/shaarli/Shaarli.git . | ||
133 | composer update --no-dev</code></pre> | ||
129 | <hr /> | 134 | <hr /> |
130 | <h2 id="stable-version">Stable version</h2> | 135 | <h2 id="stable-version">Stable version</h2> |
131 | <p>The stable version has been experienced by Shaarli users, and will receive security updates.</p> | 136 | <p>The stable version has been experienced by Shaarli users, and will receive security updates.</p> |
diff --git a/doc/Download-and-Installation.md b/doc/Download-and-Installation.md index 77af25eb..32df8984 100644 --- a/doc/Download-and-Installation.md +++ b/doc/Download-and-Installation.md | |||
@@ -8,26 +8,31 @@ Several releases are available: | |||
8 | -------------------------------------------------------- | 8 | -------------------------------------------------------- |
9 | 9 | ||
10 | ## Latest release (recommended) | 10 | ## Latest release (recommended) |
11 | 11 | ### Download as an archive | |
12 | Get the latest released version from the [releases](https://github.com/shaarli/Shaarli/releases) page.[](.html) | 12 | Get the latest released version from the [releases](https://github.com/shaarli/Shaarli/releases) page.[](.html) |
13 | 13 | ||
14 | The current latest released version is `v0.7.0`. | 14 | **Download our *shaarli-full* archive** to include dependencies. |
15 | 15 | ||
16 | ### Download as an archive | 16 | The current latest released version is `v0.8.0` |
17 | 17 | ||
18 | As a .zip archive: | 18 | Or in command lines: |
19 | 19 | ||
20 | ```bash | 20 | ```bash |
21 | $ wget https://github.com/shaarli/Shaarli/archive/v0.7.0.zip | 21 | $ wget https://github.com/shaarli/Shaarli/releases/download/v0.8.0/shaarli-v0.8.0-full.zip |
22 | $ unzip Shaarli-v0.7.0.zip | 22 | $ unzip shaarli-v0.8.0-full.zip |
23 | $ mv Shaarli-v0.7.0 /path/to/shaarli/ | 23 | $ mv Shaarli /path/to/shaarli/ |
24 | ``` | 24 | ``` |
25 | 25 | ||
26 | |||
27 | | ! |In most cases, download Shaarli from the [releases](https://github.com/shaarli/Shaarli/releases) page. Cloning using `git` or downloading Github branches as zip files requires additional steps (see below).|[](.html) | 26 | | ! |In most cases, download Shaarli from the [releases](https://github.com/shaarli/Shaarli/releases) page. Cloning using `git` or downloading Github branches as zip files requires additional steps (see below).|[](.html) |
28 | |-----|--------------------------| | 27 | |-----|--------------------------| |
29 | 28 | ||
29 | ### Using git | ||
30 | 30 | ||
31 | ``` | ||
32 | mkdir -p /path/to/shaarli && cd /path/to/shaarli/ | ||
33 | git clone -b v0.8.0 https://github.com/shaarli/Shaarli.git . | ||
34 | composer update --no-dev | ||
35 | ``` | ||
31 | 36 | ||
32 | -------------------------------------------------------- | 37 | -------------------------------------------------------- |
33 | 38 | ||
diff --git a/doc/Release-Shaarli.html b/doc/Release-Shaarli.html index cdefd3d6..0d9fa3e1 100644 --- a/doc/Release-Shaarli.html +++ b/doc/Release-Shaarli.html | |||
@@ -115,9 +115,35 @@ releases</a>.</p> | |||
115 | <li><code>origin</code> pointing to your GitHub fork</li> | 115 | <li><code>origin</code> pointing to your GitHub fork</li> |
116 | <li><code>upstream</code> pointing to the main Shaarli repository</li> | 116 | <li><code>upstream</code> pointing to the main Shaarli repository</li> |
117 | </ul></li> | 117 | </ul></li> |
118 | <li>maintainer permissions on the main Shaarli repository (to push the signed tag)</li> | 118 | <li>maintainer permissions on the main Shaarli repository, to: |
119 | <ul> | ||
120 | <li>push the signed tag</li> | ||
121 | <li>create a new release</li> | ||
122 | </ul></li> | ||
119 | <li><a href="https://getcomposer.org/">Composer</a> and <a href="http://pandoc.org/">Pandoc</a> need to be installed<a href=".html"></a></li> | 123 | <li><a href="https://getcomposer.org/">Composer</a> and <a href="http://pandoc.org/">Pandoc</a> need to be installed<a href=".html"></a></li> |
120 | </ul> | 124 | </ul> |
125 | <h2 id="github-release-draft-and-changelog.md">GitHub release draft and <code>CHANGELOG.md</code></h2> | ||
126 | <p>See <a href="http://keepachangelog.com/en/0.3.0/" class="uri">http://keepachangelog.com/en/0.3.0/</a> for changelog formatting.</p> | ||
127 | <h3 id="github-release-draft">GitHub release draft</h3> | ||
128 | <p>GitHub allows drafting the release note for the upcoming release, from the <a href="https://github.com/shaarli/Shaarli/releases">Releases</a> page. This way, the release note can be drafted while contributions are merged to <code>master</code>.<a href=".html"></a></p> | ||
129 | <h3 id="changelog.md"><code>CHANGELOG.md</code></h3> | ||
130 | <p>This file should contain the same information as the release note draft for the upcoming version.</p> | ||
131 | <p>Update it to:</p> | ||
132 | <ul> | ||
133 | <li>add new entries (additions, fixes, etc.)</li> | ||
134 | <li>mark the current version as released by setting its date and link</li> | ||
135 | <li>add a new section for the future unreleased version</li> | ||
136 | </ul> | ||
137 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="bu">cd</span> /path/to/shaarli | ||
138 | |||
139 | $ <span class="fu">nano</span> CHANGELOG.md | ||
140 | |||
141 | [<span class="ex">...</span>][](.html) | ||
142 | <span class="co">## vA.B.C - UNRELEASED</span> | ||
143 | <span class="ex">TBA</span> | ||
144 | |||
145 | <span class="co">## [vX.Y.Z](https://github.com/shaarli/Shaarli/releases/tag/vX.Y.Z) - YYYY-MM-DD[](.html)</span> | ||
146 | [<span class="ex">...</span>][](.html)</code></pre></div> | ||
121 | <h2 id="increment-the-version-code-create-and-push-a-signed-tag">Increment the version code, create and push a signed tag</h2> | 147 | <h2 id="increment-the-version-code-create-and-push-a-signed-tag">Increment the version code, create and push a signed tag</h2> |
122 | <h3 id="bump-shaarlis-version">Bump Shaarli's version</h3> | 148 | <h3 id="bump-shaarlis-version">Bump Shaarli's version</h3> |
123 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="bu">cd</span> /path/to/shaarli | 149 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="bu">cd</span> /path/to/shaarli |
@@ -165,7 +191,16 @@ $ <span class="fu">git</span> show-ref tags/v0.5.0 | |||
165 | $ <span class="fu">git</span> verify-tag f7762cf803f03f5caf4b8078359a63783d0090c1 | 191 | $ <span class="fu">git</span> verify-tag f7762cf803f03f5caf4b8078359a63783d0090c1 |
166 | <span class="ex">gpg</span>: Signature made Thu 30 Jul 2015 11:46:34 CEST using RSA key ID 4100DF6F | 192 | <span class="ex">gpg</span>: Signature made Thu 30 Jul 2015 11:46:34 CEST using RSA key ID 4100DF6F |
167 | <span class="ex">gpg</span>: Good signature from <span class="st">"VirtualTam <virtualtam@flibidi.net>"</span> [ultimate][](.html)</code></pre></div> | 193 | <span class="ex">gpg</span>: Good signature from <span class="st">"VirtualTam <virtualtam@flibidi.net>"</span> [ultimate][](.html)</code></pre></div> |
168 | <h2 id="generate-and-upload-all-in-one-release-archives">Generate and upload all-in-one release archives</h2> | 194 | <h2 id="publish-the-github-release">Publish the GitHub release</h2> |
195 | <h3 id="create-a-github-release-from-a-git-tag">Create a GitHub release from a Git tag</h3> | ||
196 | <p>From the previously drafted release:</p> | ||
197 | <ul> | ||
198 | <li>edit the release notes (if needed)</li> | ||
199 | <li>specify the appropriate Git tag</li> | ||
200 | <li>publish the release</li> | ||
201 | <li>profit!</li> | ||
202 | </ul> | ||
203 | <h3 id="generate-and-upload-all-in-one-release-archives">Generate and upload all-in-one release archives</h3> | ||
169 | <p>Users with a shared hosting may have:</p> | 204 | <p>Users with a shared hosting may have:</p> |
170 | <ul> | 205 | <ul> |
171 | <li>no SSH access</li> | 206 | <li>no SSH access</li> |
diff --git a/doc/Release-Shaarli.md b/doc/Release-Shaarli.md index 5cbcd79a..556a96ee 100644 --- a/doc/Release-Shaarli.md +++ b/doc/Release-Shaarli.md | |||
@@ -10,9 +10,39 @@ This guide assumes that you have: | |||
10 | - a local clone of your Shaarli fork, with the following remotes: | 10 | - a local clone of your Shaarli fork, with the following remotes: |
11 | - `origin` pointing to your GitHub fork | 11 | - `origin` pointing to your GitHub fork |
12 | - `upstream` pointing to the main Shaarli repository | 12 | - `upstream` pointing to the main Shaarli repository |
13 | - maintainer permissions on the main Shaarli repository (to push the signed tag) | 13 | - maintainer permissions on the main Shaarli repository, to: |
14 | - push the signed tag | ||
15 | - create a new release | ||
14 | - [Composer](https://getcomposer.org/) and [Pandoc](http://pandoc.org/) need to be installed[](.html) | 16 | - [Composer](https://getcomposer.org/) and [Pandoc](http://pandoc.org/) need to be installed[](.html) |
15 | 17 | ||
18 | ## GitHub release draft and `CHANGELOG.md` | ||
19 | See http://keepachangelog.com/en/0.3.0/ for changelog formatting. | ||
20 | |||
21 | ### GitHub release draft | ||
22 | GitHub allows drafting the release note for the upcoming release, from the [Releases](https://github.com/shaarli/Shaarli/releases) page. This way, the release note can be drafted while contributions are merged to `master`.[](.html) | ||
23 | |||
24 | ### `CHANGELOG.md` | ||
25 | This file should contain the same information as the release note draft for the upcoming version. | ||
26 | |||
27 | Update it to: | ||
28 | - add new entries (additions, fixes, etc.) | ||
29 | - mark the current version as released by setting its date and link | ||
30 | - add a new section for the future unreleased version | ||
31 | |||
32 | ```bash | ||
33 | $ cd /path/to/shaarli | ||
34 | |||
35 | $ nano CHANGELOG.md | ||
36 | |||
37 | [...][](.html) | ||
38 | ## vA.B.C - UNRELEASED | ||
39 | TBA | ||
40 | |||
41 | ## [vX.Y.Z](https://github.com/shaarli/Shaarli/releases/tag/vX.Y.Z) - YYYY-MM-DD[](.html) | ||
42 | [...][](.html) | ||
43 | ``` | ||
44 | |||
45 | |||
16 | ## Increment the version code, create and push a signed tag | 46 | ## Increment the version code, create and push a signed tag |
17 | ### Bump Shaarli's version | 47 | ### Bump Shaarli's version |
18 | ```bash | 48 | ```bash |
@@ -72,7 +102,15 @@ gpg: Signature made Thu 30 Jul 2015 11:46:34 CEST using RSA key ID 4100DF6F | |||
72 | gpg: Good signature from "VirtualTam <virtualtam@flibidi.net>" [ultimate][](.html) | 102 | gpg: Good signature from "VirtualTam <virtualtam@flibidi.net>" [ultimate][](.html) |
73 | ``` | 103 | ``` |
74 | 104 | ||
75 | ## Generate and upload all-in-one release archives | 105 | ## Publish the GitHub release |
106 | ### Create a GitHub release from a Git tag | ||
107 | From the previously drafted release: | ||
108 | - edit the release notes (if needed) | ||
109 | - specify the appropriate Git tag | ||
110 | - publish the release | ||
111 | - profit! | ||
112 | |||
113 | ### Generate and upload all-in-one release archives | ||
76 | Users with a shared hosting may have: | 114 | Users with a shared hosting may have: |
77 | - no SSH access | 115 | - no SSH access |
78 | - no possibility to install PHP packages or server extensions | 116 | - no possibility to install PHP packages or server extensions |
diff --git a/doc/Server-configuration.html b/doc/Server-configuration.html index 068900b8..2f1c25b5 100644 --- a/doc/Server-configuration.html +++ b/doc/Server-configuration.html | |||
@@ -193,6 +193,9 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
193 | ErrorLog<span class="st"> /var/log/apache2/shaarli-error.log</span> | 193 | ErrorLog<span class="st"> /var/log/apache2/shaarli-error.log</span> |
194 | CustomLog<span class="st"> /var/log/apache2/shaarli-access.log combined</span> | 194 | CustomLog<span class="st"> /var/log/apache2/shaarli-access.log combined</span> |
195 | <span class="fu"></VirtualHost></span></code></pre></div> | 195 | <span class="fu"></VirtualHost></span></code></pre></div> |
196 | <h3 id="htaccess">.htaccess</h3> | ||
197 | <p>Shaarli use <code>.htaccess</code> Apache files to deny access to files that shouldn't be directly accessed (datastore, config, etc.). You need the directive <code>AllowOverride All</code> in your virtual host configuration for them to work.</p> | ||
198 | <p><strong>Warning</strong>: If you use Apache 2.2 or lower, you need <a href="https://httpd.apache.org/docs/current/mod/mod_version.html">mod_version</a> to be installed and enabled.<a href=".html"></a></p> | ||
196 | <h2 id="lighthttpd">LightHttpd</h2> | 199 | <h2 id="lighthttpd">LightHttpd</h2> |
197 | <h2 id="nginx">Nginx</h2> | 200 | <h2 id="nginx">Nginx</h2> |
198 | <h3 id="foreword">Foreword</h3> | 201 | <h3 id="foreword">Foreword</h3> |
@@ -233,7 +236,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
233 | <li>files may be located in a user's home directory</li> | 236 | <li>files may be located in a user's home directory</li> |
234 | <li>in this case, make sure both Nginx and PHP-FPM are running as the local user/group!</li> | 237 | <li>in this case, make sure both Nginx and PHP-FPM are running as the local user/group!</li> |
235 | </ul> | 238 | </ul> |
236 | <p>For all following examples, a development configuration will be used:</p> | 239 | <p>For all following configuration examples, this user/group pair will be used:</p> |
237 | <ul> | 240 | <ul> |
238 | <li><code>user:group = john:users</code>,</li> | 241 | <li><code>user:group = john:users</code>,</li> |
239 | </ul> | 242 | </ul> |
@@ -251,6 +254,24 @@ user john users; | |||
251 | http { | 254 | http { |
252 | [...][](.html) | 255 | [...][](.html) |
253 | }</code></pre> | 256 | }</code></pre> |
257 | <h3 id="optional-increase-the-maximum-file-upload-size">(Optional) Increase the maximum file upload size</h3> | ||
258 | <p>Some bookmark dumps generated by web browsers can be <em>huge</em> due to the presence of Base64-encoded images and favicons, as well as extra verbosity when nesting links in (sub-)folders.</p> | ||
259 | <p>To increase upload size, you will need to modify both nginx and PHP configuration:</p> | ||
260 | <pre class="nginx"><code># /etc/nginx/nginx.conf | ||
261 | |||
262 | http { | ||
263 | [...][](.html) | ||
264 | |||
265 | client_max_body_size 10m; | ||
266 | |||
267 | [...][](.html) | ||
268 | }</code></pre> | ||
269 | <div class="sourceCode"><pre class="sourceCode ini"><code class="sourceCode ini"><span class="co"># /etc/php5/fpm/php.ini</span> | ||
270 | |||
271 | <span class="kw">[...][]</span><span class="dt">(.html)</span> | ||
272 | <span class="dt">post_max_size </span><span class="ot">=</span><span class="st"> 10M</span> | ||
273 | <span class="kw">[...][]</span><span class="dt">(.html)</span> | ||
274 | <span class="dt">upload_max_filesize </span><span class="ot">=</span><span class="st"> 10M</span></code></pre></div> | ||
254 | <h3 id="minimal-1">Minimal</h3> | 275 | <h3 id="minimal-1">Minimal</h3> |
255 | <p><em>WARNING: Use for development only!</em></p> | 276 | <p><em>WARNING: Use for development only!</em></p> |
256 | <pre class="nginx"><code>user john users; | 277 | <pre class="nginx"><code>user john users; |
@@ -350,6 +371,11 @@ http { | |||
350 | error_log /var/log/nginx/shaarli.error.log; | 371 | error_log /var/log/nginx/shaarli.error.log; |
351 | } | 372 | } |
352 | 373 | ||
374 | location = /shaarli/favicon.ico { | ||
375 | # serve the Shaarli favicon from its custom location | ||
376 | alias /var/www/shaarli/images/favicon.ico; | ||
377 | } | ||
378 | |||
353 | include deny.conf; | 379 | include deny.conf; |
354 | include static_assets.conf; | 380 | include static_assets.conf; |
355 | include php.conf; | 381 | include php.conf; |
@@ -403,6 +429,11 @@ http { | |||
403 | error_log /var/log/nginx/shaarli.error.log; | 429 | error_log /var/log/nginx/shaarli.error.log; |
404 | } | 430 | } |
405 | 431 | ||
432 | location = /shaarli/favicon.ico { | ||
433 | # serve the Shaarli favicon from its custom location | ||
434 | alias /var/www/shaarli/images/favicon.ico; | ||
435 | } | ||
436 | |||
406 | include deny.conf; | 437 | include deny.conf; |
407 | include static_assets.conf; | 438 | include static_assets.conf; |
408 | include php.conf; | 439 | include php.conf; |
diff --git a/doc/Server-configuration.md b/doc/Server-configuration.md index 1ab57a0a..df10feb2 100644 --- a/doc/Server-configuration.md +++ b/doc/Server-configuration.md | |||
@@ -102,6 +102,12 @@ See [Server-side TLS](https://wiki.mozilla.org/Security/Server_Side_TLS#Apache) | |||
102 | </VirtualHost> | 102 | </VirtualHost> |
103 | ``` | 103 | ``` |
104 | 104 | ||
105 | ### .htaccess | ||
106 | |||
107 | Shaarli use `.htaccess` Apache files to deny access to files that shouldn't be directly accessed (datastore, config, etc.). You need the directive `AllowOverride All` in your virtual host configuration for them to work. | ||
108 | |||
109 | **Warning**: If you use Apache 2.2 or lower, you need [mod_version](https://httpd.apache.org/docs/current/mod/mod_version.html) to be installed and enabled.[](.html) | ||
110 | |||
105 | ## LightHttpd | 111 | ## LightHttpd |
106 | 112 | ||
107 | ## Nginx | 113 | ## Nginx |
@@ -136,7 +142,7 @@ On a development server: | |||
136 | - files may be located in a user's home directory | 142 | - files may be located in a user's home directory |
137 | - in this case, make sure both Nginx and PHP-FPM are running as the local user/group! | 143 | - in this case, make sure both Nginx and PHP-FPM are running as the local user/group! |
138 | 144 | ||
139 | For all following examples, a development configuration will be used: | 145 | For all following configuration examples, this user/group pair will be used: |
140 | - `user:group = john:users`, | 146 | - `user:group = john:users`, |
141 | 147 | ||
142 | which corresponds to the following service configuration: | 148 | which corresponds to the following service configuration: |
@@ -160,6 +166,32 @@ http { | |||
160 | } | 166 | } |
161 | ``` | 167 | ``` |
162 | 168 | ||
169 | ### (Optional) Increase the maximum file upload size | ||
170 | Some bookmark dumps generated by web browsers can be _huge_ due to the presence of Base64-encoded images and favicons, as well as extra verbosity when nesting links in (sub-)folders. | ||
171 | |||
172 | To increase upload size, you will need to modify both nginx and PHP configuration: | ||
173 | |||
174 | ```nginx | ||
175 | # /etc/nginx/nginx.conf | ||
176 | |||
177 | http { | ||
178 | [...][](.html) | ||
179 | |||
180 | client_max_body_size 10m; | ||
181 | |||
182 | [...][](.html) | ||
183 | } | ||
184 | ``` | ||
185 | |||
186 | ```ini | ||
187 | # /etc/php5/fpm/php.ini | ||
188 | |||
189 | [...][](.html) | ||
190 | post_max_size = 10M | ||
191 | [...][](.html) | ||
192 | upload_max_filesize = 10M | ||
193 | ``` | ||
194 | |||
163 | ### Minimal | 195 | ### Minimal |
164 | _WARNING: Use for development only!_ | 196 | _WARNING: Use for development only!_ |
165 | 197 | ||
@@ -271,6 +303,11 @@ http { | |||
271 | error_log /var/log/nginx/shaarli.error.log; | 303 | error_log /var/log/nginx/shaarli.error.log; |
272 | } | 304 | } |
273 | 305 | ||
306 | location = /shaarli/favicon.ico { | ||
307 | # serve the Shaarli favicon from its custom location | ||
308 | alias /var/www/shaarli/images/favicon.ico; | ||
309 | } | ||
310 | |||
274 | include deny.conf; | 311 | include deny.conf; |
275 | include static_assets.conf; | 312 | include static_assets.conf; |
276 | include php.conf; | 313 | include php.conf; |
@@ -328,6 +365,11 @@ http { | |||
328 | error_log /var/log/nginx/shaarli.error.log; | 365 | error_log /var/log/nginx/shaarli.error.log; |
329 | } | 366 | } |
330 | 367 | ||
368 | location = /shaarli/favicon.ico { | ||
369 | # serve the Shaarli favicon from its custom location | ||
370 | alias /var/www/shaarli/images/favicon.ico; | ||
371 | } | ||
372 | |||
331 | include deny.conf; | 373 | include deny.conf; |
332 | include static_assets.conf; | 374 | include static_assets.conf; |
333 | include php.conf; | 375 | include php.conf; |
diff --git a/doc/Theming.html b/doc/Theming.html index 13e6acf0..7cbf7aef 100644 --- a/doc/Theming.html +++ b/doc/Theming.html | |||
@@ -119,19 +119,20 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
119 | <ul> | 119 | <ul> |
120 | <li>There should now be a <code>my-template/</code> directory under the <code>tpl/</code> dir, containing directly all the template files.</li> | 120 | <li>There should now be a <code>my-template/</code> directory under the <code>tpl/</code> dir, containing directly all the template files.</li> |
121 | </ul></li> | 121 | </ul></li> |
122 | <li><p>Edit <code>data/config.php</code> to have Shaarli use this template, e.g.</p> | 122 | <li><p>Edit <code>data/config.json.php</code> to have Shaarli use this template, in <code>"resource"</code> e.g.</p> |
123 | <div class="sourceCode"><pre class="sourceCode php"><code class="sourceCode php"><span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'RAINTPL_TPL'</span><span class="ot">]</span> = <span class="st">'tpl/my-template/'</span><span class="ot">;](</span><span class="st">'RAINTPL_TPL'</span><span class="ot">]</span>-=-<span class="st">'tpl/my-template/'</span><span class="ot">;</span>.html<span class="ot">)</span></code></pre></div></li> | 123 | <div class="sourceCode"><pre class="sourceCode json"><code class="sourceCode json"><span class="er">"raintpl_tpl":</span> <span class="er">"tpl\/my-template\/",</span></code></pre></div></li> |
124 | </ul> | 124 | </ul> |
125 | <h2 id="community-themes-templates">Community themes & templates</h2> | 125 | <h2 id="community-themes-templates">Community themes & templates</h2> |
126 | <ul> | 126 | <ul> |
127 | <li><a href="https://github.com/AkibaTech/Shaarli---SuperHero-Theme">AkibaTech/Shaarli Superhero Theme</a> - A template/theme for Shaarli<a href=".html"></a></li> | 127 | <li><a href="https://github.com/AkibaTech/Shaarli---SuperHero-Theme">AkibaTech/Shaarli Superhero Theme</a> - A template/theme for Shaarli<a href=".html"></a></li> |
128 | <li><a href="https://github.com/alexisju/albinomouse-template">alexisju/albinomouse-template</a> - A full template for Shaarli<a href=".html"></a></li> | 128 | <li><a href="https://github.com/alexisju/albinomouse-template">alexisju/albinomouse-template</a> - A full template for Shaarli<a href=".html"></a></li> |
129 | <li><a href="https://github.com/ArthurHoaro/shaarli-launch">ArthurHoaro/shaarli-launch</a> - Customizable Shaarli theme.<a href=".html"></a></li> | ||
129 | <li><a href="https://github.com/dhoko/ShaarliTemplate">dhoko/ShaarliTemplate</a> - A template/theme for Shaarli<a href=".html"></a></li> | 130 | <li><a href="https://github.com/dhoko/ShaarliTemplate">dhoko/ShaarliTemplate</a> - A template/theme for Shaarli<a href=".html"></a></li> |
130 | <li><a href="https://github.com/kalvn/shaarli-blocks">kalvn/shaarli-blocks</a> - A template/theme for Shaarli<a href=".html"></a></li> | 131 | <li><a href="https://github.com/kalvn/shaarli-blocks">kalvn/shaarli-blocks</a> - A template/theme for Shaarli<a href=".html"></a></li> |
131 | <li><a href="https://github.com/kalvn/Shaarli-Material">kalvn/Shaarli-Material</a> - A theme (template) based on Google's Material Design for Shaarli, the superfast delicious clone.<a href=".html"></a></li> | 132 | <li><a href="https://github.com/kalvn/Shaarli-Material">kalvn/Shaarli-Material</a> - A theme (template) based on Google's Material Design for Shaarli, the superfast delicious clone.<a href=".html"></a></li> |
133 | <li><a href="https://github.com/ManufacturaInd/shaarli-2004licious-theme">ManufacturaInd/shaarli-2004licious-theme</a> - A template/theme as a humble homage to the early looks of the del.icio.us site.<a href=".html"></a></li> | ||
132 | <li><a href="https://github.com/misterair/limonade">misterair/Limonade</a> - A fork of (legacy) Shaarli with a new template<a href=".html"></a></li> | 134 | <li><a href="https://github.com/misterair/limonade">misterair/Limonade</a> - A fork of (legacy) Shaarli with a new template<a href=".html"></a></li> |
133 | <li><a href="https://github.com/mrjovanovic/serious-theme-shaarli">mrjovanovic/serious-theme-shaarli</a> - A serious theme for SHaarli.<a href=".html"></a></li> | 135 | <li><a href="https://github.com/mrjovanovic/serious-theme-shaarli">mrjovanovic/serious-theme-shaarli</a> - A serious theme for SHaarli.<a href=".html"></a></li> |
134 | <li><a href="https://github.com/Vinm/Blue-theme-for-Shaarli">Vinm/Blue-theme-for Shaarli</a> - A template/theme for Shaarli (<a href="https://github.com/Vinm/Blue-theme-for-Shaarli/issues/2">unmaintained</a>, compatibility unknown)<a href=".html"></a></li> | ||
135 | <li><a href="https://github.com/vivienhaese/shaarlitheme">vivienhaese/shaarlitheme</a> - A Shaarli fork meant to be run in an openshift instance<a href=".html"></a></li> | 136 | <li><a href="https://github.com/vivienhaese/shaarlitheme">vivienhaese/shaarlitheme</a> - A Shaarli fork meant to be run in an openshift instance<a href=".html"></a></li> |
136 | </ul> | 137 | </ul> |
137 | <h3 id="example-installation-albinomouse-template">Example installation: AlbinoMouse template</h3> | 138 | <h3 id="example-installation-albinomouse-template">Example installation: AlbinoMouse template</h3> |
diff --git a/doc/Theming.md b/doc/Theming.md index 7fb8d927..a21899c2 100644 --- a/doc/Theming.md +++ b/doc/Theming.md | |||
@@ -16,20 +16,21 @@ _WARNING - This feature is currently being worked on and will be improved in the | |||
16 | - Find it's git clone URL or download the zip archive for the template. | 16 | - Find it's git clone URL or download the zip archive for the template. |
17 | - In your Shaarli `tpl/` directory, run `git clone https://url/of/my-template/` or unpack the zip archive. | 17 | - In your Shaarli `tpl/` directory, run `git clone https://url/of/my-template/` or unpack the zip archive. |
18 | - There should now be a `my-template/` directory under the `tpl/` dir, containing directly all the template files. | 18 | - There should now be a `my-template/` directory under the `tpl/` dir, containing directly all the template files. |
19 | - Edit `data/config.php` to have Shaarli use this template, e.g. | 19 | - Edit `data/config.json.php` to have Shaarli use this template, in `"resource"` e.g. |
20 | ```php | 20 | ```json |
21 | $GLOBALS['config'['RAINTPL_TPL'] = 'tpl/my-template/';]('RAINTPL_TPL']-=-'tpl/my-template/';.html) | 21 | "raintpl_tpl": "tpl\/my-template\/", |
22 | ``` | 22 | ``` |
23 | 23 | ||
24 | ## Community themes & templates | 24 | ## Community themes & templates |
25 | - [AkibaTech/Shaarli Superhero Theme](https://github.com/AkibaTech/Shaarli---SuperHero-Theme) - A template/theme for Shaarli[](.html) | 25 | - [AkibaTech/Shaarli Superhero Theme](https://github.com/AkibaTech/Shaarli---SuperHero-Theme) - A template/theme for Shaarli[](.html) |
26 | - [alexisju/albinomouse-template](https://github.com/alexisju/albinomouse-template) - A full template for Shaarli[](.html) | 26 | - [alexisju/albinomouse-template](https://github.com/alexisju/albinomouse-template) - A full template for Shaarli[](.html) |
27 | - [ArthurHoaro/shaarli-launch](https://github.com/ArthurHoaro/shaarli-launch) - Customizable Shaarli theme.[](.html) | ||
27 | - [dhoko/ShaarliTemplate](https://github.com/dhoko/ShaarliTemplate) - A template/theme for Shaarli[](.html) | 28 | - [dhoko/ShaarliTemplate](https://github.com/dhoko/ShaarliTemplate) - A template/theme for Shaarli[](.html) |
28 | - [kalvn/shaarli-blocks](https://github.com/kalvn/shaarli-blocks) - A template/theme for Shaarli[](.html) | 29 | - [kalvn/shaarli-blocks](https://github.com/kalvn/shaarli-blocks) - A template/theme for Shaarli[](.html) |
29 | - [kalvn/Shaarli-Material](https://github.com/kalvn/Shaarli-Material) - A theme (template) based on Google's Material Design for Shaarli, the superfast delicious clone.[](.html) | 30 | - [kalvn/Shaarli-Material](https://github.com/kalvn/Shaarli-Material) - A theme (template) based on Google's Material Design for Shaarli, the superfast delicious clone.[](.html) |
31 | - [ManufacturaInd/shaarli-2004licious-theme](https://github.com/ManufacturaInd/shaarli-2004licious-theme) - A template/theme as a humble homage to the early looks of the del.icio.us site.[](.html) | ||
30 | - [misterair/Limonade](https://github.com/misterair/limonade) - A fork of (legacy) Shaarli with a new template[](.html) | 32 | - [misterair/Limonade](https://github.com/misterair/limonade) - A fork of (legacy) Shaarli with a new template[](.html) |
31 | - [mrjovanovic/serious-theme-shaarli](https://github.com/mrjovanovic/serious-theme-shaarli) - A serious theme for SHaarli.[](.html) | 33 | - [mrjovanovic/serious-theme-shaarli](https://github.com/mrjovanovic/serious-theme-shaarli) - A serious theme for SHaarli.[](.html) |
32 | - [Vinm/Blue-theme-for Shaarli](https://github.com/Vinm/Blue-theme-for-Shaarli) - A template/theme for Shaarli ([unmaintained](https://github.com/Vinm/Blue-theme-for-Shaarli/issues/2), compatibility unknown)[](.html) | ||
33 | - [vivienhaese/shaarlitheme](https://github.com/vivienhaese/shaarlitheme) - A Shaarli fork meant to be run in an openshift instance[](.html) | 34 | - [vivienhaese/shaarlitheme](https://github.com/vivienhaese/shaarlitheme) - A Shaarli fork meant to be run in an openshift instance[](.html) |
34 | 35 | ||
35 | ### Example installation: AlbinoMouse template | 36 | ### Example installation: AlbinoMouse template |
diff --git a/docker/.htaccess b/docker/.htaccess index b584d98c..f601c1ee 100644 --- a/docker/.htaccess +++ b/docker/.htaccess | |||
@@ -1,2 +1,13 @@ | |||
1 | Allow from none | 1 | <IfModule version_module> |
2 | Deny from all | 2 | <IfVersion >= 2.4> |
3 | Require all denied | ||
4 | </IfVersion> | ||
5 | <IfVersion < 2.4> | ||
6 | Allow from none | ||
7 | Deny from all | ||
8 | </IfVersion> | ||
9 | </IfModule> | ||
10 | |||
11 | <IfModule !version_module> | ||
12 | Require all denied | ||
13 | </IfModule> | ||
diff --git a/docker/development/Dockerfile b/docker/development/Dockerfile index 0c19b085..d9ef8da7 100644 --- a/docker/development/Dockerfile +++ b/docker/development/Dockerfile | |||
@@ -15,6 +15,8 @@ RUN apt-get update \ | |||
15 | nano \ | 15 | nano \ |
16 | && apt-get clean | 16 | && apt-get clean |
17 | 17 | ||
18 | RUN sed -i 's/post_max_size.*/post_max_size = 10M/' /etc/php5/fpm/php.ini | ||
19 | RUN sed -i 's/upload_max_filesize.*/upload_max_filesize = 10M/' /etc/php5/fpm/php.ini | ||
18 | COPY nginx.conf /etc/nginx/nginx.conf | 20 | COPY nginx.conf /etc/nginx/nginx.conf |
19 | COPY supervised.conf /etc/supervisor/conf.d/supervised.conf | 21 | COPY supervised.conf /etc/supervisor/conf.d/supervised.conf |
20 | 22 | ||
diff --git a/docker/development/nginx.conf b/docker/development/nginx.conf index cda09b56..ac0c6c61 100644 --- a/docker/development/nginx.conf +++ b/docker/development/nginx.conf | |||
@@ -11,6 +11,8 @@ http { | |||
11 | default_type application/octet-stream; | 11 | default_type application/octet-stream; |
12 | keepalive_timeout 20; | 12 | keepalive_timeout 20; |
13 | 13 | ||
14 | client_max_body_size 10m; | ||
15 | |||
14 | index index.html index.php; | 16 | index index.html index.php; |
15 | 17 | ||
16 | server { | 18 | server { |
@@ -49,6 +51,11 @@ http { | |||
49 | add_header Cache-Control "public, must-revalidate, proxy-revalidate"; | 51 | add_header Cache-Control "public, must-revalidate, proxy-revalidate"; |
50 | } | 52 | } |
51 | 53 | ||
54 | location = /favicon.ico { | ||
55 | # serve the Shaarli favicon from its custom location | ||
56 | alias /var/www/shaarli/images/favicon.ico; | ||
57 | } | ||
58 | |||
52 | location ~ (index)\.php$ { | 59 | location ~ (index)\.php$ { |
53 | # filter and proxy PHP requests to PHP-FPM | 60 | # filter and proxy PHP requests to PHP-FPM |
54 | fastcgi_pass unix:/var/run/php5-fpm.sock; | 61 | fastcgi_pass unix:/var/run/php5-fpm.sock; |
diff --git a/docker/production/Dockerfile b/docker/production/Dockerfile index d93ed262..d0509115 100644 --- a/docker/production/Dockerfile +++ b/docker/production/Dockerfile | |||
@@ -14,6 +14,8 @@ RUN apt-get update \ | |||
14 | supervisor \ | 14 | supervisor \ |
15 | && apt-get clean | 15 | && apt-get clean |
16 | 16 | ||
17 | RUN sed -i 's/post_max_size.*/post_max_size = 10M/' /etc/php5/fpm/php.ini | ||
18 | RUN sed -i 's/upload_max_filesize.*/upload_max_filesize = 10M/' /etc/php5/fpm/php.ini | ||
17 | COPY nginx.conf /etc/nginx/nginx.conf | 19 | COPY nginx.conf /etc/nginx/nginx.conf |
18 | COPY supervised.conf /etc/supervisor/conf.d/supervised.conf | 20 | COPY supervised.conf /etc/supervisor/conf.d/supervised.conf |
19 | 21 | ||
diff --git a/docker/production/nginx.conf b/docker/production/nginx.conf index e23c4587..5ffa02d0 100644 --- a/docker/production/nginx.conf +++ b/docker/production/nginx.conf | |||
@@ -11,6 +11,8 @@ http { | |||
11 | default_type application/octet-stream; | 11 | default_type application/octet-stream; |
12 | keepalive_timeout 20; | 12 | keepalive_timeout 20; |
13 | 13 | ||
14 | client_max_body_size 10m; | ||
15 | |||
14 | index index.html index.php; | 16 | index index.html index.php; |
15 | 17 | ||
16 | server { | 18 | server { |
@@ -41,6 +43,11 @@ http { | |||
41 | add_header Cache-Control "public, must-revalidate, proxy-revalidate"; | 43 | add_header Cache-Control "public, must-revalidate, proxy-revalidate"; |
42 | } | 44 | } |
43 | 45 | ||
46 | location = /favicon.ico { | ||
47 | # serve the Shaarli favicon from its custom location | ||
48 | alias /var/www/shaarli/images/favicon.ico; | ||
49 | } | ||
50 | |||
44 | location ~ (index)\.php$ { | 51 | location ~ (index)\.php$ { |
45 | # filter and proxy PHP requests to PHP-FPM | 52 | # filter and proxy PHP requests to PHP-FPM |
46 | fastcgi_pass unix:/var/run/php5-fpm.sock; | 53 | fastcgi_pass unix:/var/run/php5-fpm.sock; |
diff --git a/docker/production/stable/Dockerfile b/docker/production/stable/Dockerfile index a509fda6..fc9588b0 100644 --- a/docker/production/stable/Dockerfile +++ b/docker/production/stable/Dockerfile | |||
@@ -14,6 +14,8 @@ RUN apt-get update \ | |||
14 | supervisor \ | 14 | supervisor \ |
15 | && apt-get clean | 15 | && apt-get clean |
16 | 16 | ||
17 | RUN sed -i 's/post_max_size.*/post_max_size = 10M/' /etc/php5/fpm/php.ini | ||
18 | RUN sed -i 's/upload_max_filesize.*/upload_max_filesize = 10M/' /etc/php5/fpm/php.ini | ||
17 | COPY nginx.conf /etc/nginx/nginx.conf | 19 | COPY nginx.conf /etc/nginx/nginx.conf |
18 | COPY supervised.conf /etc/supervisor/conf.d/supervised.conf | 20 | COPY supervised.conf /etc/supervisor/conf.d/supervised.conf |
19 | 21 | ||
diff --git a/docker/production/stable/nginx.conf b/docker/production/stable/nginx.conf index e23c4587..5ffa02d0 100644 --- a/docker/production/stable/nginx.conf +++ b/docker/production/stable/nginx.conf | |||
@@ -11,6 +11,8 @@ http { | |||
11 | default_type application/octet-stream; | 11 | default_type application/octet-stream; |
12 | keepalive_timeout 20; | 12 | keepalive_timeout 20; |
13 | 13 | ||
14 | client_max_body_size 10m; | ||
15 | |||
14 | index index.html index.php; | 16 | index index.html index.php; |
15 | 17 | ||
16 | server { | 18 | server { |
@@ -41,6 +43,11 @@ http { | |||
41 | add_header Cache-Control "public, must-revalidate, proxy-revalidate"; | 43 | add_header Cache-Control "public, must-revalidate, proxy-revalidate"; |
42 | } | 44 | } |
43 | 45 | ||
46 | location = /favicon.ico { | ||
47 | # serve the Shaarli favicon from its custom location | ||
48 | alias /var/www/shaarli/images/favicon.ico; | ||
49 | } | ||
50 | |||
44 | location ~ (index)\.php$ { | 51 | location ~ (index)\.php$ { |
45 | # filter and proxy PHP requests to PHP-FPM | 52 | # filter and proxy PHP requests to PHP-FPM |
46 | fastcgi_pass unix:/var/run/php5-fpm.sock; | 53 | fastcgi_pass unix:/var/run/php5-fpm.sock; |
diff --git a/inc/shaarli.css b/inc/shaarli.css index 5808320c..a24d4b7c 100644 --- a/inc/shaarli.css +++ b/inc/shaarli.css | |||
@@ -37,6 +37,10 @@ em { | |||
37 | font-style: italic; | 37 | font-style: italic; |
38 | } | 38 | } |
39 | 39 | ||
40 | strong { | ||
41 | font-weight: bold; | ||
42 | } | ||
43 | |||
40 | /* Buttons */ | 44 | /* Buttons */ |
41 | .bigbutton { | 45 | .bigbutton { |
42 | background-color: #c0c0c0; | 46 | background-color: #c0c0c0; |
@@ -1168,8 +1172,13 @@ ul.errors { | |||
1168 | } | 1172 | } |
1169 | 1173 | ||
1170 | #pluginsadmin a { | 1174 | #pluginsadmin a { |
1175 | color: #486D08; | ||
1176 | } | ||
1177 | |||
1178 | #pluginsadmin a.arrow { | ||
1171 | color: black; | 1179 | color: black; |
1172 | } | 1180 | } |
1181 | |||
1173 | /* 404 page */ | 1182 | /* 404 page */ |
1174 | .error-container { | 1183 | .error-container { |
1175 | 1184 | ||
@@ -1,6 +1,6 @@ | |||
1 | <?php | 1 | <?php |
2 | /** | 2 | /** |
3 | * Shaarli v0.8.0 - Shaare your links... | 3 | * Shaarli v0.8.1 - Shaare your links... |
4 | * | 4 | * |
5 | * The personal, minimalist, super-fast, database free, bookmarking service. | 5 | * The personal, minimalist, super-fast, database free, bookmarking service. |
6 | * | 6 | * |
@@ -25,7 +25,7 @@ if (date_default_timezone_get() == '') { | |||
25 | /* | 25 | /* |
26 | * PHP configuration | 26 | * PHP configuration |
27 | */ | 27 | */ |
28 | define('shaarli_version', '0.8.0'); | 28 | define('shaarli_version', '0.8.1'); |
29 | 29 | ||
30 | // http://server.com/x/shaarli --> /shaarli/ | 30 | // http://server.com/x/shaarli --> /shaarli/ |
31 | define('WEB_PATH', substr($_SERVER['REQUEST_URI'], 0, 1+strrpos($_SERVER['REQUEST_URI'], '/', 0))); | 31 | define('WEB_PATH', substr($_SERVER['REQUEST_URI'], 0, 1+strrpos($_SERVER['REQUEST_URI'], '/', 0))); |
@@ -564,24 +564,19 @@ function showDailyRSS($conf) { | |||
564 | ); | 564 | ); |
565 | 565 | ||
566 | /* Some Shaarlies may have very few links, so we need to look | 566 | /* Some Shaarlies may have very few links, so we need to look |
567 | back in time (rsort()) until we have enough days ($nb_of_days). | 567 | back in time until we have enough days ($nb_of_days). |
568 | */ | 568 | */ |
569 | $linkdates = array(); | ||
570 | foreach ($LINKSDB as $linkdate => $value) { | ||
571 | $linkdates[] = $linkdate; | ||
572 | } | ||
573 | rsort($linkdates); | ||
574 | $nb_of_days = 7; // We take 7 days. | 569 | $nb_of_days = 7; // We take 7 days. |
575 | $today = date('Ymd'); | 570 | $today = date('Ymd'); |
576 | $days = array(); | 571 | $days = array(); |
577 | 572 | ||
578 | foreach ($linkdates as $linkdate) { | 573 | foreach ($LINKSDB as $link) { |
579 | $day = substr($linkdate, 0, 8); // Extract day (without time) | 574 | $day = $link['created']->format('Ymd'); // Extract day (without time) |
580 | if (strcmp($day,$today) < 0) { | 575 | if (strcmp($day, $today) < 0) { |
581 | if (empty($days[$day])) { | 576 | if (empty($days[$day])) { |
582 | $days[$day] = array(); | 577 | $days[$day] = array(); |
583 | } | 578 | } |
584 | $days[$day][] = $linkdate; | 579 | $days[$day][] = $link; |
585 | } | 580 | } |
586 | 581 | ||
587 | if (count($days) > $nb_of_days) { | 582 | if (count($days) > $nb_of_days) { |
@@ -601,24 +596,18 @@ function showDailyRSS($conf) { | |||
601 | echo '<copyright>'. $pageaddr .'</copyright>'. PHP_EOL; | 596 | echo '<copyright>'. $pageaddr .'</copyright>'. PHP_EOL; |
602 | 597 | ||
603 | // For each day. | 598 | // For each day. |
604 | foreach ($days as $day => $linkdates) { | 599 | foreach ($days as $day => $links) { |
605 | $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); | 600 | $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); |
606 | $absurl = escape(index_url($_SERVER).'?do=daily&day='.$day); // Absolute URL of the corresponding "Daily" page. | 601 | $absurl = escape(index_url($_SERVER).'?do=daily&day='.$day); // Absolute URL of the corresponding "Daily" page. |
607 | 602 | ||
608 | // Build the HTML body of this RSS entry. | ||
609 | $links = array(); | ||
610 | |||
611 | // We pre-format some fields for proper output. | 603 | // We pre-format some fields for proper output. |
612 | foreach ($linkdates as $linkdate) { | 604 | foreach ($links as &$link) { |
613 | $l = $LINKSDB[$linkdate]; | 605 | $link['formatedDescription'] = format_description($link['description'], $conf->get('redirector.url')); |
614 | $l['formatedDescription'] = format_description($l['description'], $conf->get('redirector.url')); | 606 | $link['thumbnail'] = thumbnail($conf, $link['url']); |
615 | $l['thumbnail'] = thumbnail($conf, $l['url']); | 607 | $link['timestamp'] = $link['created']->getTimestamp(); |
616 | $l_date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $l['linkdate']); | 608 | if (startsWith($link['url'], '?')) { |
617 | $l['timestamp'] = $l_date->getTimestamp(); | 609 | $link['url'] = index_url($_SERVER) . $link['url']; // make permalink URL absolute |
618 | if (startsWith($l['url'], '?')) { | ||
619 | $l['url'] = index_url($_SERVER) . $l['url']; // make permalink URL absolute | ||
620 | } | 610 | } |
621 | $links[$linkdate] = $l; | ||
622 | } | 611 | } |
623 | 612 | ||
624 | // Then build the HTML for this day: | 613 | // Then build the HTML for this day: |
@@ -680,8 +669,7 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) | |||
680 | $linksToDisplay[$key]['taglist']=$taglist; | 669 | $linksToDisplay[$key]['taglist']=$taglist; |
681 | $linksToDisplay[$key]['formatedDescription'] = format_description($link['description'], $conf->get('redirector.url')); | 670 | $linksToDisplay[$key]['formatedDescription'] = format_description($link['description'], $conf->get('redirector.url')); |
682 | $linksToDisplay[$key]['thumbnail'] = thumbnail($conf, $link['url']); | 671 | $linksToDisplay[$key]['thumbnail'] = thumbnail($conf, $link['url']); |
683 | $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']); | 672 | $linksToDisplay[$key]['timestamp'] = $link['created']->getTimestamp(); |
684 | $linksToDisplay[$key]['timestamp'] = $date->getTimestamp(); | ||
685 | } | 673 | } |
686 | 674 | ||
687 | /* We need to spread the articles on 3 columns. | 675 | /* We need to spread the articles on 3 columns. |
@@ -831,7 +819,7 @@ function renderPage($conf, $pluginManager) | |||
831 | // Get only links which have a thumbnail. | 819 | // Get only links which have a thumbnail. |
832 | foreach($links as $link) | 820 | foreach($links as $link) |
833 | { | 821 | { |
834 | $permalink='?'.escape(smallHash($link['linkdate'])); | 822 | $permalink='?'.$link['shorturl']; |
835 | $thumb=lazyThumbnail($conf, $link['url'],$permalink); | 823 | $thumb=lazyThumbnail($conf, $link['url'],$permalink); |
836 | if ($thumb!='') // Only output links which have a thumbnail. | 824 | if ($thumb!='') // Only output links which have a thumbnail. |
837 | { | 825 | { |
@@ -1078,6 +1066,7 @@ function renderPage($conf, $pluginManager) | |||
1078 | { | 1066 | { |
1079 | $data = array( | 1067 | $data = array( |
1080 | 'pageabsaddr' => index_url($_SERVER), | 1068 | 'pageabsaddr' => index_url($_SERVER), |
1069 | 'sslenabled' => !empty($_SERVER['HTTPS']) | ||
1081 | ); | 1070 | ); |
1082 | $pluginManager->executeHooks('render_tools', $data); | 1071 | $pluginManager->executeHooks('render_tools', $data); |
1083 | 1072 | ||
@@ -1244,13 +1233,30 @@ function renderPage($conf, $pluginManager) | |||
1244 | // -------- User clicked the "Save" button when editing a link: Save link to database. | 1233 | // -------- User clicked the "Save" button when editing a link: Save link to database. |
1245 | if (isset($_POST['save_edit'])) | 1234 | if (isset($_POST['save_edit'])) |
1246 | { | 1235 | { |
1247 | $linkdate = $_POST['lf_linkdate']; | ||
1248 | $updated = isset($LINKSDB[$linkdate]) ? strval(date('Ymd_His')) : false; | ||
1249 | |||
1250 | // Go away! | 1236 | // Go away! |
1251 | if (! tokenOk($_POST['token'])) { | 1237 | if (! tokenOk($_POST['token'])) { |
1252 | die('Wrong token.'); | 1238 | die('Wrong token.'); |
1253 | } | 1239 | } |
1240 | |||
1241 | // lf_id should only be present if the link exists. | ||
1242 | $id = !empty($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : $LINKSDB->getNextId(); | ||
1243 | // Linkdate is kept here to: | ||
1244 | // - use the same permalink for notes as they're displayed when creating them | ||
1245 | // - let users hack creation date of their posts | ||
1246 | // See: https://github.com/shaarli/Shaarli/wiki/Datastore-hacks#changing-the-timestamp-for-a-link | ||
1247 | $linkdate = escape($_POST['lf_linkdate']); | ||
1248 | if (isset($LINKSDB[$id])) { | ||
1249 | // Edit | ||
1250 | $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); | ||
1251 | $updated = new DateTime(); | ||
1252 | $shortUrl = $LINKSDB[$id]['shorturl']; | ||
1253 | } else { | ||
1254 | // New link | ||
1255 | $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); | ||
1256 | $updated = null; | ||
1257 | $shortUrl = link_small_hash($created, $id); | ||
1258 | } | ||
1259 | |||
1254 | // Remove multiple spaces. | 1260 | // Remove multiple spaces. |
1255 | $tags = trim(preg_replace('/\s\s+/', ' ', $_POST['lf_tags'])); | 1261 | $tags = trim(preg_replace('/\s\s+/', ' ', $_POST['lf_tags'])); |
1256 | // Remove first '-' char in tags. | 1262 | // Remove first '-' char in tags. |
@@ -1267,14 +1273,17 @@ function renderPage($conf, $pluginManager) | |||
1267 | } | 1273 | } |
1268 | 1274 | ||
1269 | $link = array( | 1275 | $link = array( |
1276 | 'id' => $id, | ||
1270 | 'title' => trim($_POST['lf_title']), | 1277 | 'title' => trim($_POST['lf_title']), |
1271 | 'url' => $url, | 1278 | 'url' => $url, |
1272 | 'description' => $_POST['lf_description'], | 1279 | 'description' => $_POST['lf_description'], |
1273 | 'private' => (isset($_POST['lf_private']) ? 1 : 0), | 1280 | 'private' => (isset($_POST['lf_private']) ? 1 : 0), |
1274 | 'linkdate' => $linkdate, | 1281 | 'created' => $created, |
1275 | 'updated' => $updated, | 1282 | 'updated' => $updated, |
1276 | 'tags' => str_replace(',', ' ', $tags) | 1283 | 'tags' => str_replace(',', ' ', $tags), |
1284 | 'shorturl' => $shortUrl, | ||
1277 | ); | 1285 | ); |
1286 | |||
1278 | // If title is empty, use the URL as title. | 1287 | // If title is empty, use the URL as title. |
1279 | if ($link['title'] == '') { | 1288 | if ($link['title'] == '') { |
1280 | $link['title'] = $link['url']; | 1289 | $link['title'] = $link['url']; |
@@ -1282,7 +1291,7 @@ function renderPage($conf, $pluginManager) | |||
1282 | 1291 | ||
1283 | $pluginManager->executeHooks('save_link', $link); | 1292 | $pluginManager->executeHooks('save_link', $link); |
1284 | 1293 | ||
1285 | $LINKSDB[$linkdate] = $link; | 1294 | $LINKSDB[$id] = $link; |
1286 | $LINKSDB->save($conf->get('resource.page_cache')); | 1295 | $LINKSDB->save($conf->get('resource.page_cache')); |
1287 | pubsubhub($conf); | 1296 | pubsubhub($conf); |
1288 | 1297 | ||
@@ -1295,7 +1304,7 @@ function renderPage($conf, $pluginManager) | |||
1295 | $returnurl = !empty($_POST['returnurl']) ? $_POST['returnurl'] : '?'; | 1304 | $returnurl = !empty($_POST['returnurl']) ? $_POST['returnurl'] : '?'; |
1296 | $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); | 1305 | $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); |
1297 | // Scroll to the link which has been edited. | 1306 | // Scroll to the link which has been edited. |
1298 | $location .= '#' . smallHash($_POST['lf_linkdate']); | 1307 | $location .= '#' . $link['shorturl']; |
1299 | // After saving the link, redirect to the page the user was on. | 1308 | // After saving the link, redirect to the page the user was on. |
1300 | header('Location: '. $location); | 1309 | header('Location: '. $location); |
1301 | exit; | 1310 | exit; |
@@ -1306,27 +1315,31 @@ function renderPage($conf, $pluginManager) | |||
1306 | { | 1315 | { |
1307 | // If we are called from the bookmarklet, we must close the popup: | 1316 | // If we are called from the bookmarklet, we must close the popup: |
1308 | if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } | 1317 | if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } |
1318 | $link = $LINKSDB[(int) escape($_POST['lf_id'])]; | ||
1309 | $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); | 1319 | $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); |
1310 | $returnurl .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited. | 1320 | // Scroll to the link which has been edited. |
1321 | $returnurl .= '#'. $link['shorturl']; | ||
1311 | $returnurl = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); | 1322 | $returnurl = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); |
1312 | header('Location: '.$returnurl); // After canceling, redirect to the page the user was on. | 1323 | header('Location: '.$returnurl); // After canceling, redirect to the page the user was on. |
1313 | exit; | 1324 | exit; |
1314 | } | 1325 | } |
1315 | 1326 | ||
1316 | // -------- User clicked the "Delete" button when editing a link: Delete link from database. | 1327 | // -------- User clicked the "Delete" button when editing a link: Delete link from database. |
1317 | if ($targetPage == Router::$PAGE_DELETELINK) | 1328 | if (isset($_POST['delete_link'])) |
1318 | { | 1329 | { |
1319 | if (!tokenOk($_GET['token'])) die('Wrong token.'); | 1330 | if (!tokenOk($_POST['token'])) die('Wrong token.'); |
1331 | |||
1320 | // We do not need to ask for confirmation: | 1332 | // We do not need to ask for confirmation: |
1321 | // - confirmation is handled by JavaScript | 1333 | // - confirmation is handled by JavaScript |
1322 | // - we are protected from XSRF by the token. | 1334 | // - we are protected from XSRF by the token. |
1323 | $linkdate = $_GET['delete_link']; | ||
1324 | $link = $LINKSDB[$linkdate]; | ||
1325 | |||
1326 | $pluginManager->executeHooks('delete_link', $link); | ||
1327 | 1335 | ||
1328 | unset($LINKSDB[$linkdate]); | 1336 | // FIXME! We keep `lf_linkdate` for consistency before a proper API. To be removed. |
1329 | $LINKSDB->save($conf->get('resource.page_cache')); // save to disk | 1337 | $id = isset($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : intval(escape($_POST['lf_linkdate'])); |
1338 | |||
1339 | $pluginManager->executeHooks('delete_link', $LINKSDB[$id]); | ||
1340 | |||
1341 | unset($LINKSDB[$id]); | ||
1342 | $LINKSDB->save('resource.page_cache'); // save to disk | ||
1330 | 1343 | ||
1331 | // If we are called from the bookmarklet, we must close the popup: | 1344 | // If we are called from the bookmarklet, we must close the popup: |
1332 | if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } | 1345 | if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } |
@@ -1364,8 +1377,10 @@ function renderPage($conf, $pluginManager) | |||
1364 | // -------- User clicked the "EDIT" button on a link: Display link edit form. | 1377 | // -------- User clicked the "EDIT" button on a link: Display link edit form. |
1365 | if (isset($_GET['edit_link'])) | 1378 | if (isset($_GET['edit_link'])) |
1366 | { | 1379 | { |
1367 | $link = $LINKSDB[$_GET['edit_link']]; // Read database | 1380 | $id = (int) escape($_GET['edit_link']); |
1381 | $link = $LINKSDB[$id]; // Read database | ||
1368 | if (!$link) { header('Location: ?'); exit; } // Link not found in database. | 1382 | if (!$link) { header('Location: ?'); exit; } // Link not found in database. |
1383 | $link['linkdate'] = $link['created']->format(LinkDB::LINK_DATE_FORMAT); | ||
1369 | $data = array( | 1384 | $data = array( |
1370 | 'link' => $link, | 1385 | 'link' => $link, |
1371 | 'link_is_new' => false, | 1386 | 'link_is_new' => false, |
@@ -1389,10 +1404,10 @@ function renderPage($conf, $pluginManager) | |||
1389 | $link_is_new = false; | 1404 | $link_is_new = false; |
1390 | // Check if URL is not already in database (in this case, we will edit the existing link) | 1405 | // Check if URL is not already in database (in this case, we will edit the existing link) |
1391 | $link = $LINKSDB->getLinkFromUrl($url); | 1406 | $link = $LINKSDB->getLinkFromUrl($url); |
1392 | if (!$link) | 1407 | if (! $link) |
1393 | { | 1408 | { |
1394 | $link_is_new = true; | 1409 | $link_is_new = true; |
1395 | $linkdate = strval(date('Ymd_His')); | 1410 | $linkdate = strval(date(LinkDB::LINK_DATE_FORMAT)); |
1396 | // Get title if it was provided in URL (by the bookmarklet). | 1411 | // Get title if it was provided in URL (by the bookmarklet). |
1397 | $title = empty($_GET['title']) ? '' : escape($_GET['title']); | 1412 | $title = empty($_GET['title']) ? '' : escape($_GET['title']); |
1398 | // Get description if it was provided in URL (by the bookmarklet). [Bronco added that] | 1413 | // Get description if it was provided in URL (by the bookmarklet). [Bronco added that] |
@@ -1416,7 +1431,7 @@ function renderPage($conf, $pluginManager) | |||
1416 | } | 1431 | } |
1417 | 1432 | ||
1418 | if ($url == '') { | 1433 | if ($url == '') { |
1419 | $url = '?' . smallHash($linkdate); | 1434 | $url = '?' . smallHash($linkdate . $LINKSDB->getNextId()); |
1420 | $title = 'Note: '; | 1435 | $title = 'Note: '; |
1421 | } | 1436 | } |
1422 | $url = escape($url); | 1437 | $url = escape($url); |
@@ -1430,6 +1445,8 @@ function renderPage($conf, $pluginManager) | |||
1430 | 'tags' => $tags, | 1445 | 'tags' => $tags, |
1431 | 'private' => $private | 1446 | 'private' => $private |
1432 | ); | 1447 | ); |
1448 | } else { | ||
1449 | $link['linkdate'] = $link['created']->format(LinkDB::LINK_DATE_FORMAT); | ||
1433 | } | 1450 | } |
1434 | 1451 | ||
1435 | $data = array( | 1452 | $data = array( |
@@ -1635,18 +1652,15 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1635 | $link['description'] = format_description($link['description'], $conf->get('redirector.url')); | 1652 | $link['description'] = format_description($link['description'], $conf->get('redirector.url')); |
1636 | $classLi = ($i % 2) != 0 ? '' : 'publicLinkHightLight'; | 1653 | $classLi = ($i % 2) != 0 ? '' : 'publicLinkHightLight'; |
1637 | $link['class'] = $link['private'] == 0 ? $classLi : 'private'; | 1654 | $link['class'] = $link['private'] == 0 ? $classLi : 'private'; |
1638 | $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']); | 1655 | $link['timestamp'] = $link['created']->getTimestamp(); |
1639 | $link['timestamp'] = $date->getTimestamp(); | ||
1640 | if (! empty($link['updated'])) { | 1656 | if (! empty($link['updated'])) { |
1641 | $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['updated']); | 1657 | $link['updated_timestamp'] = $link['updated']->getTimestamp(); |
1642 | $link['updated_timestamp'] = $date->getTimestamp(); | ||
1643 | } else { | 1658 | } else { |
1644 | $link['updated_timestamp'] = ''; | 1659 | $link['updated_timestamp'] = ''; |
1645 | } | 1660 | } |
1646 | $taglist = explode(' ', $link['tags']); | 1661 | $taglist = explode(' ', $link['tags']); |
1647 | uasort($taglist, 'strcasecmp'); | 1662 | uasort($taglist, 'strcasecmp'); |
1648 | $link['taglist'] = $taglist; | 1663 | $link['taglist'] = $taglist; |
1649 | $link['shorturl'] = smallHash($link['linkdate']); | ||
1650 | // Check for both signs of a note: starting with ? and 7 chars long. | 1664 | // Check for both signs of a note: starting with ? and 7 chars long. |
1651 | if ($link['url'][0] === '?' && | 1665 | if ($link['url'][0] === '?' && |
1652 | strlen($link['url']) === 7) { | 1666 | strlen($link['url']) === 7) { |
diff --git a/pagecache/.htaccess b/pagecache/.htaccess index b584d98c..f601c1ee 100644 --- a/pagecache/.htaccess +++ b/pagecache/.htaccess | |||
@@ -1,2 +1,13 @@ | |||
1 | Allow from none | 1 | <IfModule version_module> |
2 | Deny from all | 2 | <IfVersion >= 2.4> |
3 | Require all denied | ||
4 | </IfVersion> | ||
5 | <IfVersion < 2.4> | ||
6 | Allow from none | ||
7 | Deny from all | ||
8 | </IfVersion> | ||
9 | </IfModule> | ||
10 | |||
11 | <IfModule !version_module> | ||
12 | Require all denied | ||
13 | </IfModule> | ||
diff --git a/plugins/addlink_toolbar/addlink_toolbar.css b/plugins/addlink_toolbar/addlink_toolbar.css deleted file mode 100644 index b6a612f0..00000000 --- a/plugins/addlink_toolbar/addlink_toolbar.css +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | #addlink_toolbar { | ||
2 | display: inline; | ||
3 | margin: 0 0 0 25px; | ||
4 | } \ No newline at end of file | ||
diff --git a/plugins/addlink_toolbar/addlink_toolbar.php b/plugins/addlink_toolbar/addlink_toolbar.php index c96a891e..bf8a198a 100644 --- a/plugins/addlink_toolbar/addlink_toolbar.php +++ b/plugins/addlink_toolbar/addlink_toolbar.php | |||
@@ -16,10 +16,12 @@ function hook_addlink_toolbar_render_header($data) | |||
16 | { | 16 | { |
17 | if ($data['_PAGE_'] == Router::$PAGE_LINKLIST && $data['_LOGGEDIN_'] === true) { | 17 | if ($data['_PAGE_'] == Router::$PAGE_LINKLIST && $data['_LOGGEDIN_'] === true) { |
18 | $form = array( | 18 | $form = array( |
19 | 'method' => 'GET', | 19 | 'attr' => array( |
20 | 'action' => '', | 20 | 'method' => 'GET', |
21 | 'name' => 'addform', | 21 | 'action' => '', |
22 | 'class' => 'addform', | 22 | 'name' => 'addform', |
23 | 'class' => 'addform', | ||
24 | ), | ||
23 | 'inputs' => array( | 25 | 'inputs' => array( |
24 | array( | 26 | array( |
25 | 'type' => 'text', | 27 | 'type' => 'text', |
diff --git a/plugins/archiveorg/archiveorg.html b/plugins/archiveorg/archiveorg.html index 576bd46e..0781fe35 100644 --- a/plugins/archiveorg/archiveorg.html +++ b/plugins/archiveorg/archiveorg.html | |||
@@ -1 +1 @@ | |||
<span><a href="https://web.archive.org/web/%s"><img class="linklist-plugin-icon" src="plugins/archiveorg/internetarchive.png" title="View on archive.org" /></a></span> | <span><a href="https://web.archive.org/web/%s"><img class="linklist-plugin-icon" src="plugins/archiveorg/internetarchive.png" title="View on archive.org" alt="archive.org" /></a></span> | ||
diff --git a/plugins/demo_plugin/demo_plugin.php b/plugins/demo_plugin/demo_plugin.php index 15482fe0..8fdbf663 100644 --- a/plugins/demo_plugin/demo_plugin.php +++ b/plugins/demo_plugin/demo_plugin.php | |||
@@ -56,9 +56,11 @@ function hook_demo_plugin_render_header($data) | |||
56 | * and a mandatory `html` key, which contains its value. | 56 | * and a mandatory `html` key, which contains its value. |
57 | */ | 57 | */ |
58 | $button = array( | 58 | $button = array( |
59 | 'href' => '#', | 59 | 'attr' => array ( |
60 | 'class' => 'mybutton', | 60 | 'href' => '#', |
61 | 'title' => 'hover me', | 61 | 'class' => 'mybutton', |
62 | 'title' => 'hover me', | ||
63 | ), | ||
62 | 'html' => 'DEMO buttons toolbar', | 64 | 'html' => 'DEMO buttons toolbar', |
63 | ); | 65 | ); |
64 | $data['buttons_toolbar'][] = $button; | 66 | $data['buttons_toolbar'][] = $button; |
@@ -87,9 +89,11 @@ function hook_demo_plugin_render_header($data) | |||
87 | * </form> | 89 | * </form> |
88 | */ | 90 | */ |
89 | $form = array( | 91 | $form = array( |
90 | 'method' => 'GET', | 92 | 'attr' => array( |
91 | 'action' => '?', | 93 | 'method' => 'GET', |
92 | 'class' => 'addform', | 94 | 'action' => '?', |
95 | 'class' => 'addform', | ||
96 | ), | ||
93 | 'inputs' => array( | 97 | 'inputs' => array( |
94 | array( | 98 | array( |
95 | 'type' => 'text', | 99 | 'type' => 'text', |
@@ -102,7 +106,9 @@ function hook_demo_plugin_render_header($data) | |||
102 | } | 106 | } |
103 | // Another button always displayed | 107 | // Another button always displayed |
104 | $button = array( | 108 | $button = array( |
105 | 'href' => '#', | 109 | 'attr' => array( |
110 | 'href' => '#', | ||
111 | ), | ||
106 | 'html' => 'Demo', | 112 | 'html' => 'Demo', |
107 | ); | 113 | ); |
108 | $data['buttons_toolbar'][] = $button; | 114 | $data['buttons_toolbar'][] = $button; |
@@ -197,8 +203,10 @@ function hook_demo_plugin_render_linklist($data) | |||
197 | * It's also recommended to add key 'on' or 'off' for theme rendering. | 203 | * It's also recommended to add key 'on' or 'off' for theme rendering. |
198 | */ | 204 | */ |
199 | $action = array( | 205 | $action = array( |
200 | 'href' => '?up', | 206 | 'attr' => array( |
201 | 'title' => 'Uppercase!', | 207 | 'href' => '?up', |
208 | 'title' => 'Uppercase!', | ||
209 | ), | ||
202 | 'html' => '←', | 210 | 'html' => '←', |
203 | ); | 211 | ); |
204 | 212 | ||
diff --git a/plugins/isso/isso.php b/plugins/isso/isso.php index ffb7cfac..ce16645f 100644 --- a/plugins/isso/isso.php +++ b/plugins/isso/isso.php | |||
@@ -41,9 +41,9 @@ function hook_isso_render_linklist($data, $conf) | |||
41 | // Only display comments for permalinks. | 41 | // Only display comments for permalinks. |
42 | if (count($data['links']) == 1 && empty($data['search_tags']) && empty($data['search_term'])) { | 42 | if (count($data['links']) == 1 && empty($data['search_tags']) && empty($data['search_term'])) { |
43 | $link = reset($data['links']); | 43 | $link = reset($data['links']); |
44 | $isso_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/isso/isso.html'); | 44 | $issoHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/isso/isso.html'); |
45 | 45 | ||
46 | $isso = sprintf($isso_html, $issoUrl, $issoUrl, $link['linkdate'], $link['linkdate']); | 46 | $isso = sprintf($issoHtml, $issoUrl, $issoUrl, $link['id'], $link['id']); |
47 | $data['plugin_end_zone'][] = $isso; | 47 | $data['plugin_end_zone'][] = $isso; |
48 | 48 | ||
49 | // Hackish way to include this CSS file only when necessary. | 49 | // Hackish way to include this CSS file only when necessary. |
diff --git a/plugins/markdown/README.md b/plugins/markdown/README.md index 4f021871..aafcf066 100644 --- a/plugins/markdown/README.md +++ b/plugins/markdown/README.md | |||
@@ -20,26 +20,52 @@ The directory structure should look like: | |||
20 | |--- markdown.css | 20 | |--- markdown.css |
21 | |--- markdown.meta | 21 | |--- markdown.meta |
22 | |--- markdown.php | 22 | |--- markdown.php |
23 | |--- Parsedown.php | ||
24 | |--- README.md | 23 | |--- README.md |
25 | ``` | 24 | ``` |
26 | 25 | ||
27 | To enable the plugin, just check it in the plugin administration page. | 26 | To enable the plugin, just check it in the plugin administration page. |
28 | 27 | ||
29 | You can also add `markdown` to your list of enabled plugins in `data/config.php` | 28 | You can also add `markdown` to your list of enabled plugins in `data/config.json.php` |
30 | (`ENABLED_PLUGINS` array). | 29 | (`general.enabled_plugins` list). |
31 | 30 | ||
32 | This should look like: | 31 | This should look like: |
33 | 32 | ||
34 | ``` | 33 | ``` |
35 | $GLOBALS['config']['ENABLED_PLUGINS'] = array('qrcode', 'any_other_plugin', 'markdown') | 34 | "general": { |
35 | "enabled_plugins": [ | ||
36 | "markdown", | ||
37 | [...] | ||
38 | ], | ||
39 | } | ||
36 | ``` | 40 | ``` |
37 | 41 | ||
42 | Parsedown parsing library is imported using Composer. If you installed Shaarli using `git`, | ||
43 | or the `master` branch, run | ||
44 | |||
45 | composer update --no-dev --prefer-dist | ||
46 | |||
38 | ### No Markdown tag | 47 | ### No Markdown tag |
39 | 48 | ||
40 | If the tag `.nomarkdown` is set for a shaare, it won't be converted to Markdown syntax. | 49 | If the tag `nomarkdown` is set for a shaare, it won't be converted to Markdown syntax. |
41 | 50 | ||
42 | > Note: it's a private tag (leading dot), so it won't be displayed to visitors. | 51 | > Note: this is a special tag, so it won't be displayed in link list. |
52 | |||
53 | ### HTML rendering | ||
54 | |||
55 | Markdown support HTML tags. For example: | ||
56 | |||
57 | > <strong>strong</strong><strike>strike</strike> | ||
58 | |||
59 | Will render as: | ||
60 | |||
61 | > <strong>strong</strong><strike>strike</strike> | ||
62 | |||
63 | If you want to shaare HTML code, it is necessary to use inline code or code blocks. | ||
64 | |||
65 | **If your shaared descriptions containing HTML tags before enabling the markdown plugin, | ||
66 | enabling it might break your page.** | ||
67 | |||
68 | > Note: HTML tags such as script, iframe, etc. are disabled for security reasons. | ||
43 | 69 | ||
44 | ### Known issue | 70 | ### Known issue |
45 | 71 | ||
diff --git a/plugins/markdown/markdown.meta b/plugins/markdown/markdown.meta index e3904ed8..8df2ed0b 100644 --- a/plugins/markdown/markdown.meta +++ b/plugins/markdown/markdown.meta | |||
@@ -1 +1,4 @@ | |||
1 | description="Render shaare description with Markdown syntax." | 1 | description="Render shaare description with Markdown syntax.<br><strong>Warning</strong>: |
2 | If your shaared descriptions containing HTML tags before enabling the markdown plugin, | ||
3 | enabling it might break your page. | ||
4 | See the <a href=\"https://github.com/shaarli/Shaarli/tree/master/plugins/markdown#html-rendering\">README</a>." | ||
diff --git a/plugins/markdown/markdown.php b/plugins/markdown/markdown.php index a764b6fa..0cf6e6e2 100644 --- a/plugins/markdown/markdown.php +++ b/plugins/markdown/markdown.php | |||
@@ -22,7 +22,7 @@ function hook_markdown_render_linklist($data) | |||
22 | { | 22 | { |
23 | foreach ($data['links'] as &$value) { | 23 | foreach ($data['links'] as &$value) { |
24 | if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { | 24 | if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { |
25 | $value['taglist'] = stripNoMarkdownTag($value['taglist']); | 25 | $value = stripNoMarkdownTag($value); |
26 | continue; | 26 | continue; |
27 | } | 27 | } |
28 | $value['description'] = process_markdown($value['description']); | 28 | $value['description'] = process_markdown($value['description']); |
@@ -41,7 +41,7 @@ function hook_markdown_render_feed($data) | |||
41 | { | 41 | { |
42 | foreach ($data['links'] as &$value) { | 42 | foreach ($data['links'] as &$value) { |
43 | if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { | 43 | if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { |
44 | $value['tags'] = stripNoMarkdownTag($value['tags']); | 44 | $value = stripNoMarkdownTag($value); |
45 | continue; | 45 | continue; |
46 | } | 46 | } |
47 | $value['description'] = process_markdown($value['description']); | 47 | $value['description'] = process_markdown($value['description']); |
@@ -63,6 +63,7 @@ function hook_markdown_render_daily($data) | |||
63 | foreach ($data['cols'] as &$value) { | 63 | foreach ($data['cols'] as &$value) { |
64 | foreach ($value as &$value2) { | 64 | foreach ($value as &$value2) { |
65 | if (!empty($value2['tags']) && noMarkdownTag($value2['tags'])) { | 65 | if (!empty($value2['tags']) && noMarkdownTag($value2['tags'])) { |
66 | $value2 = stripNoMarkdownTag($value2); | ||
66 | continue; | 67 | continue; |
67 | } | 68 | } |
68 | $value2['formatedDescription'] = process_markdown($value2['formatedDescription']); | 69 | $value2['formatedDescription'] = process_markdown($value2['formatedDescription']); |
@@ -81,20 +82,30 @@ function hook_markdown_render_daily($data) | |||
81 | */ | 82 | */ |
82 | function noMarkdownTag($tags) | 83 | function noMarkdownTag($tags) |
83 | { | 84 | { |
84 | return strpos($tags, NO_MD_TAG) !== false; | 85 | return preg_match('/(^|\s)'. NO_MD_TAG .'(\s|$)/', $tags); |
85 | } | 86 | } |
86 | 87 | ||
87 | /** | 88 | /** |
88 | * Remove the no-markdown meta tag so it won't be displayed. | 89 | * Remove the no-markdown meta tag so it won't be displayed. |
89 | * | 90 | * |
90 | * @param string $tags Tag list. | 91 | * @param array $link Link data. |
91 | * | 92 | * |
92 | * @return string tag list without no markdown tag. | 93 | * @return array Updated link without no markdown tag. |
93 | */ | 94 | */ |
94 | function stripNoMarkdownTag($tags) | 95 | function stripNoMarkdownTag($link) |
95 | { | 96 | { |
96 | unset($tags[array_search(NO_MD_TAG, $tags)]); | 97 | if (! empty($link['taglist'])) { |
97 | return array_values($tags); | 98 | $offset = array_search(NO_MD_TAG, $link['taglist']); |
99 | if ($offset !== false) { | ||
100 | unset($link['taglist'][$offset]); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | if (!empty($link['tags'])) { | ||
105 | str_replace(NO_MD_TAG, '', $link['tags']); | ||
106 | } | ||
107 | |||
108 | return $link; | ||
98 | } | 109 | } |
99 | 110 | ||
100 | /** | 111 | /** |
diff --git a/plugins/playvideos/playvideos.php b/plugins/playvideos/playvideos.php index 2f4b0a31..64484504 100644 --- a/plugins/playvideos/playvideos.php +++ b/plugins/playvideos/playvideos.php | |||
@@ -17,9 +17,11 @@ function hook_playvideos_render_header($data) | |||
17 | { | 17 | { |
18 | if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) { | 18 | if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) { |
19 | $playvideo = array( | 19 | $playvideo = array( |
20 | 'href' => '#', | 20 | 'attr' => array( |
21 | 'title' => 'Video player', | 21 | 'href' => '#', |
22 | 'id' => 'playvideos', | 22 | 'title' => 'Video player', |
23 | 'id' => 'playvideos', | ||
24 | ), | ||
23 | 'html' => '► Play Videos' | 25 | 'html' => '► Play Videos' |
24 | ); | 26 | ); |
25 | $data['buttons_toolbar'][] = $playvideo; | 27 | $data['buttons_toolbar'][] = $playvideo; |
diff --git a/plugins/qrcode/qrcode.html b/plugins/qrcode/qrcode.html index cebc5644..dc214ed1 100644 --- a/plugins/qrcode/qrcode.html +++ b/plugins/qrcode/qrcode.html | |||
@@ -1,5 +1,5 @@ | |||
1 | <div class="linkqrcode"> | 1 | <div class="linkqrcode"> |
2 | <a href="http://qrfree.kaywa.com/?l=1&s=8&d=%s" onclick="showQrCode(this); return false;" class="qrcode" data-permalink="%s"> | 2 | <a href="http://qrfree.kaywa.com/?l=1&s=8&d=%s" onclick="showQrCode(this); return false;" class="qrcode" data-permalink="%s"> |
3 | <img src="%s/qrcode/qrcode.png" class="linklist-plugin-icon" title="QR-Code"> | 3 | <img src="%s/qrcode/qrcode.png" class="linklist-plugin-icon" title="QR-Code" alt="QRCode"> |
4 | </a> | 4 | </a> |
5 | </div> | 5 | </div> |
diff --git a/plugins/readityourself/readityourself.html b/plugins/readityourself/readityourself.html index e8c5f784..5e200715 100644 --- a/plugins/readityourself/readityourself.html +++ b/plugins/readityourself/readityourself.html | |||
@@ -1 +1 @@ | |||
<span><a href="%s?url=%s"><img class="linklist-plugin-icon" src="%s/readityourself/book-open.png" title="Read with Readityourself" /></a></span> | <span><a href="%s?url=%s"><img class="linklist-plugin-icon" src="%s/readityourself/book-open.png" title="Read with Readityourself" alt="readityourself" /></a></span> | ||
diff --git a/plugins/wallabag/wallabag.html b/plugins/wallabag/wallabag.html index c7b1d044..e861536d 100644 --- a/plugins/wallabag/wallabag.html +++ b/plugins/wallabag/wallabag.html | |||
@@ -1 +1 @@ | |||
<span><a href="%s%s" target="_blank"><img class="linklist-plugin-icon" src="%s/wallabag/wallabag.png" title="Save to wallabag" /></a></span> | <span><a href="%s%s" target="_blank"><img class="linklist-plugin-icon" src="%s/wallabag/wallabag.png" title="Save to wallabag" alt="wallabag" /></a></span> | ||
diff --git a/shaarli_version.php b/shaarli_version.php index eaab95c6..431387bb 100644 --- a/shaarli_version.php +++ b/shaarli_version.php | |||
@@ -1 +1 @@ | |||
<?php /* 0.8.0 */ ?> | <?php /* 0.8.1 */ ?> | ||
diff --git a/tests/.htaccess b/tests/.htaccess index b584d98c..f601c1ee 100644 --- a/tests/.htaccess +++ b/tests/.htaccess | |||
@@ -1,2 +1,13 @@ | |||
1 | Allow from none | 1 | <IfModule version_module> |
2 | Deny from all | 2 | <IfVersion >= 2.4> |
3 | Require all denied | ||
4 | </IfVersion> | ||
5 | <IfVersion < 2.4> | ||
6 | Allow from none | ||
7 | Deny from all | ||
8 | </IfVersion> | ||
9 | </IfModule> | ||
10 | |||
11 | <IfModule !version_module> | ||
12 | Require all denied | ||
13 | </IfModule> | ||
diff --git a/tests/FeedBuilderTest.php b/tests/FeedBuilderTest.php index d7839402..06a44506 100644 --- a/tests/FeedBuilderTest.php +++ b/tests/FeedBuilderTest.php | |||
@@ -84,8 +84,9 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase | |||
84 | $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); | 84 | $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); |
85 | 85 | ||
86 | // Test first link (note link) | 86 | // Test first link (note link) |
87 | $link = array_shift($data['links']); | 87 | $link = reset($data['links']); |
88 | $this->assertEquals('20150310_114651', $link['linkdate']); | 88 | $this->assertEquals(41, $link['id']); |
89 | $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); | ||
89 | $this->assertEquals('http://host.tld/?WDWyig', $link['guid']); | 90 | $this->assertEquals('http://host.tld/?WDWyig', $link['guid']); |
90 | $this->assertEquals('http://host.tld/?WDWyig', $link['url']); | 91 | $this->assertEquals('http://host.tld/?WDWyig', $link['url']); |
91 | $this->assertRegExp('/Tue, 10 Mar 2015 11:46:51 \+\d{4}/', $link['pub_iso_date']); | 92 | $this->assertRegExp('/Tue, 10 Mar 2015 11:46:51 \+\d{4}/', $link['pub_iso_date']); |
@@ -99,14 +100,14 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase | |||
99 | $this->assertEquals('sTuff', $link['taglist'][0]); | 100 | $this->assertEquals('sTuff', $link['taglist'][0]); |
100 | 101 | ||
101 | // Test URL with external link. | 102 | // Test URL with external link. |
102 | $this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $data['links']['20150310_114633']['url']); | 103 | $this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $data['links'][8]['url']); |
103 | 104 | ||
104 | // Test multitags. | 105 | // Test multitags. |
105 | $this->assertEquals(5, count($data['links']['20141125_084734']['taglist'])); | 106 | $this->assertEquals(5, count($data['links'][6]['taglist'])); |
106 | $this->assertEquals('css', $data['links']['20141125_084734']['taglist'][0]); | 107 | $this->assertEquals('css', $data['links'][6]['taglist'][0]); |
107 | 108 | ||
108 | // Test update date | 109 | // Test update date |
109 | $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links']['20150310_114633']['up_iso_date']); | 110 | $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']); |
110 | } | 111 | } |
111 | 112 | ||
112 | /** | 113 | /** |
@@ -119,9 +120,9 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase | |||
119 | $data = $feedBuilder->buildData(); | 120 | $data = $feedBuilder->buildData(); |
120 | $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); | 121 | $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); |
121 | $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']); | 122 | $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']); |
122 | $link = array_shift($data['links']); | 123 | $link = reset($data['links']); |
123 | $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']); | 124 | $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']); |
124 | $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links']['20150310_114633']['up_iso_date']); | 125 | $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']); |
125 | } | 126 | } |
126 | 127 | ||
127 | /** | 128 | /** |
@@ -138,7 +139,8 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase | |||
138 | $data = $feedBuilder->buildData(); | 139 | $data = $feedBuilder->buildData(); |
139 | $this->assertEquals(1, count($data['links'])); | 140 | $this->assertEquals(1, count($data['links'])); |
140 | $link = array_shift($data['links']); | 141 | $link = array_shift($data['links']); |
141 | $this->assertEquals('20150310_114651', $link['linkdate']); | 142 | $this->assertEquals(41, $link['id']); |
143 | $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); | ||
142 | } | 144 | } |
143 | 145 | ||
144 | /** | 146 | /** |
@@ -154,7 +156,8 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase | |||
154 | $data = $feedBuilder->buildData(); | 156 | $data = $feedBuilder->buildData(); |
155 | $this->assertEquals(1, count($data['links'])); | 157 | $this->assertEquals(1, count($data['links'])); |
156 | $link = array_shift($data['links']); | 158 | $link = array_shift($data['links']); |
157 | $this->assertEquals('20150310_114651', $link['linkdate']); | 159 | $this->assertEquals(41, $link['id']); |
160 | $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); | ||
158 | } | 161 | } |
159 | 162 | ||
160 | /** | 163 | /** |
@@ -170,15 +173,17 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase | |||
170 | $this->assertTrue($data['usepermalinks']); | 173 | $this->assertTrue($data['usepermalinks']); |
171 | // First link is a permalink | 174 | // First link is a permalink |
172 | $link = array_shift($data['links']); | 175 | $link = array_shift($data['links']); |
173 | $this->assertEquals('20150310_114651', $link['linkdate']); | 176 | $this->assertEquals(41, $link['id']); |
177 | $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); | ||
174 | $this->assertEquals('http://host.tld/?WDWyig', $link['guid']); | 178 | $this->assertEquals('http://host.tld/?WDWyig', $link['guid']); |
175 | $this->assertEquals('http://host.tld/?WDWyig', $link['url']); | 179 | $this->assertEquals('http://host.tld/?WDWyig', $link['url']); |
176 | $this->assertContains('Direct link', $link['description']); | 180 | $this->assertContains('Direct link', $link['description']); |
177 | $this->assertContains('http://host.tld/?WDWyig', $link['description']); | 181 | $this->assertContains('http://host.tld/?WDWyig', $link['description']); |
178 | // Second link is a direct link | 182 | // Second link is a direct link |
179 | $link = array_shift($data['links']); | 183 | $link = array_shift($data['links']); |
180 | $this->assertEquals('20150310_114633', $link['linkdate']); | 184 | $this->assertEquals(8, $link['id']); |
181 | $this->assertEquals('http://host.tld/?kLHmZg', $link['guid']); | 185 | $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114633'), $link['created']); |
186 | $this->assertEquals('http://host.tld/?RttfEw', $link['guid']); | ||
182 | $this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['url']); | 187 | $this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['url']); |
183 | $this->assertContains('Direct link', $link['description']); | 188 | $this->assertContains('Direct link', $link['description']); |
184 | $this->assertContains('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['description']); | 189 | $this->assertContains('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['description']); |
diff --git a/tests/LinkDBTest.php b/tests/LinkDBTest.php index 9d79386c..1f62a34a 100644 --- a/tests/LinkDBTest.php +++ b/tests/LinkDBTest.php | |||
@@ -186,14 +186,15 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
186 | $dbSize = sizeof($testDB); | 186 | $dbSize = sizeof($testDB); |
187 | 187 | ||
188 | $link = array( | 188 | $link = array( |
189 | 'id' => 42, | ||
189 | 'title'=>'an additional link', | 190 | 'title'=>'an additional link', |
190 | 'url'=>'http://dum.my', | 191 | 'url'=>'http://dum.my', |
191 | 'description'=>'One more', | 192 | 'description'=>'One more', |
192 | 'private'=>0, | 193 | 'private'=>0, |
193 | 'linkdate'=>'20150518_190000', | 194 | 'created'=> DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150518_190000'), |
194 | 'tags'=>'unit test' | 195 | 'tags'=>'unit test' |
195 | ); | 196 | ); |
196 | $testDB[$link['linkdate']] = $link; | 197 | $testDB[$link['id']] = $link; |
197 | $testDB->save('tests'); | 198 | $testDB->save('tests'); |
198 | 199 | ||
199 | $testDB = new LinkDB(self::$testDatastore, true, false); | 200 | $testDB = new LinkDB(self::$testDatastore, true, false); |
@@ -238,12 +239,12 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
238 | public function testDays() | 239 | public function testDays() |
239 | { | 240 | { |
240 | $this->assertEquals( | 241 | $this->assertEquals( |
241 | array('20121206', '20130614', '20150310'), | 242 | array('20100310', '20121206', '20130614', '20150310'), |
242 | self::$publicLinkDB->days() | 243 | self::$publicLinkDB->days() |
243 | ); | 244 | ); |
244 | 245 | ||
245 | $this->assertEquals( | 246 | $this->assertEquals( |
246 | array('20121206', '20130614', '20141125', '20150310'), | 247 | array('20100310', '20121206', '20130614', '20141125', '20150310'), |
247 | self::$privateLinkDB->days() | 248 | self::$privateLinkDB->days() |
248 | ); | 249 | ); |
249 | } | 250 | } |
@@ -290,10 +291,11 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
290 | 'stallman' => 1, | 291 | 'stallman' => 1, |
291 | 'free' => 1, | 292 | 'free' => 1, |
292 | '-exclude' => 1, | 293 | '-exclude' => 1, |
294 | 'hashtag' => 2, | ||
293 | // The DB contains a link with `sTuff` and another one with `stuff` tag. | 295 | // The DB contains a link with `sTuff` and another one with `stuff` tag. |
294 | // They need to be grouped with the first case found (`sTuff`). | 296 | // They need to be grouped with the first case found - order by date DESC: `sTuff`. |
295 | 'sTuff' => 2, | 297 | 'sTuff' => 2, |
296 | 'hashtag' => 2, | 298 | 'ut' => 1, |
297 | ), | 299 | ), |
298 | self::$publicLinkDB->allTags() | 300 | self::$publicLinkDB->allTags() |
299 | ); | 301 | ); |
@@ -321,6 +323,7 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
321 | 'tag2' => 1, | 323 | 'tag2' => 1, |
322 | 'tag3' => 1, | 324 | 'tag3' => 1, |
323 | 'tag4' => 1, | 325 | 'tag4' => 1, |
326 | 'ut' => 1, | ||
324 | ), | 327 | ), |
325 | self::$privateLinkDB->allTags() | 328 | self::$privateLinkDB->allTags() |
326 | ); | 329 | ); |
@@ -411,6 +414,11 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
411 | 1, | 414 | 1, |
412 | count(self::$publicLinkDB->filterHash($request)) | 415 | count(self::$publicLinkDB->filterHash($request)) |
413 | ); | 416 | ); |
417 | $request = smallHash('20150310_114633' . 8); | ||
418 | $this->assertEquals( | ||
419 | 1, | ||
420 | count(self::$publicLinkDB->filterHash($request)) | ||
421 | ); | ||
414 | } | 422 | } |
415 | 423 | ||
416 | /** | 424 | /** |
@@ -433,4 +441,23 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
433 | { | 441 | { |
434 | self::$publicLinkDB->filterHash(''); | 442 | self::$publicLinkDB->filterHash(''); |
435 | } | 443 | } |
444 | |||
445 | /** | ||
446 | * Test reorder with asc/desc parameter. | ||
447 | */ | ||
448 | public function testReorderLinksDesc() | ||
449 | { | ||
450 | self::$privateLinkDB->reorder('ASC'); | ||
451 | $linkIds = array(42, 4, 1, 0, 7, 6, 8, 41); | ||
452 | $cpt = 0; | ||
453 | foreach (self::$privateLinkDB as $key => $value) { | ||
454 | $this->assertEquals($linkIds[$cpt++], $key); | ||
455 | } | ||
456 | self::$privateLinkDB->reorder('DESC'); | ||
457 | $linkIds = array_reverse($linkIds); | ||
458 | $cpt = 0; | ||
459 | foreach (self::$privateLinkDB as $key => $value) { | ||
460 | $this->assertEquals($linkIds[$cpt++], $key); | ||
461 | } | ||
462 | } | ||
436 | } | 463 | } |
diff --git a/tests/LinkFilterTest.php b/tests/LinkFilterTest.php index 7d45fc59..21d680a5 100644 --- a/tests/LinkFilterTest.php +++ b/tests/LinkFilterTest.php | |||
@@ -159,7 +159,7 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase | |||
159 | 159 | ||
160 | $this->assertEquals( | 160 | $this->assertEquals( |
161 | 'MediaGoblin', | 161 | 'MediaGoblin', |
162 | $links['20130614_184135']['title'] | 162 | $links[7]['title'] |
163 | ); | 163 | ); |
164 | } | 164 | } |
165 | 165 | ||
@@ -286,7 +286,7 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase | |||
286 | ); | 286 | ); |
287 | 287 | ||
288 | $this->assertEquals( | 288 | $this->assertEquals( |
289 | 6, | 289 | 7, |
290 | count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, '-revolution')) | 290 | count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, '-revolution')) |
291 | ); | 291 | ); |
292 | } | 292 | } |
@@ -346,7 +346,7 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase | |||
346 | ); | 346 | ); |
347 | 347 | ||
348 | $this->assertEquals( | 348 | $this->assertEquals( |
349 | 6, | 349 | 7, |
350 | count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, '-free')) | 350 | count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, '-free')) |
351 | ); | 351 | ); |
352 | } | 352 | } |
diff --git a/tests/NetscapeBookmarkUtils/BookmarkExportTest.php b/tests/NetscapeBookmarkUtils/BookmarkExportTest.php index cc54ab9f..6a47bbb9 100644 --- a/tests/NetscapeBookmarkUtils/BookmarkExportTest.php +++ b/tests/NetscapeBookmarkUtils/BookmarkExportTest.php | |||
@@ -50,7 +50,7 @@ class BookmarkExportTest extends PHPUnit_Framework_TestCase | |||
50 | $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'all', false, ''); | 50 | $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'all', false, ''); |
51 | $this->assertEquals(self::$refDb->countLinks(), sizeof($links)); | 51 | $this->assertEquals(self::$refDb->countLinks(), sizeof($links)); |
52 | foreach ($links as $link) { | 52 | foreach ($links as $link) { |
53 | $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']); | 53 | $date = $link['created']; |
54 | $this->assertEquals( | 54 | $this->assertEquals( |
55 | $date->getTimestamp(), | 55 | $date->getTimestamp(), |
56 | $link['timestamp'] | 56 | $link['timestamp'] |
@@ -70,7 +70,7 @@ class BookmarkExportTest extends PHPUnit_Framework_TestCase | |||
70 | $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'private', false, ''); | 70 | $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'private', false, ''); |
71 | $this->assertEquals(self::$refDb->countPrivateLinks(), sizeof($links)); | 71 | $this->assertEquals(self::$refDb->countPrivateLinks(), sizeof($links)); |
72 | foreach ($links as $link) { | 72 | foreach ($links as $link) { |
73 | $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']); | 73 | $date = $link['created']; |
74 | $this->assertEquals( | 74 | $this->assertEquals( |
75 | $date->getTimestamp(), | 75 | $date->getTimestamp(), |
76 | $link['timestamp'] | 76 | $link['timestamp'] |
@@ -90,7 +90,7 @@ class BookmarkExportTest extends PHPUnit_Framework_TestCase | |||
90 | $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public', false, ''); | 90 | $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public', false, ''); |
91 | $this->assertEquals(self::$refDb->countPublicLinks(), sizeof($links)); | 91 | $this->assertEquals(self::$refDb->countPublicLinks(), sizeof($links)); |
92 | foreach ($links as $link) { | 92 | foreach ($links as $link) { |
93 | $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']); | 93 | $date = $link['created']; |
94 | $this->assertEquals( | 94 | $this->assertEquals( |
95 | $date->getTimestamp(), | 95 | $date->getTimestamp(), |
96 | $link['timestamp'] | 96 | $link['timestamp'] |
diff --git a/tests/NetscapeBookmarkUtils/BookmarkImportTest.php b/tests/NetscapeBookmarkUtils/BookmarkImportTest.php index f0ad500f..0ca07eac 100644 --- a/tests/NetscapeBookmarkUtils/BookmarkImportTest.php +++ b/tests/NetscapeBookmarkUtils/BookmarkImportTest.php | |||
@@ -43,6 +43,18 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
43 | protected $pagecache = 'tests'; | 43 | protected $pagecache = 'tests'; |
44 | 44 | ||
45 | /** | 45 | /** |
46 | * @var string Save the current timezone. | ||
47 | */ | ||
48 | protected static $defaultTimeZone; | ||
49 | |||
50 | public static function setUpBeforeClass() | ||
51 | { | ||
52 | self::$defaultTimeZone = date_default_timezone_get(); | ||
53 | // Timezone without DST for test consistency | ||
54 | date_default_timezone_set('Africa/Nairobi'); | ||
55 | } | ||
56 | |||
57 | /** | ||
46 | * Resets test data before each test | 58 | * Resets test data before each test |
47 | */ | 59 | */ |
48 | protected function setUp() | 60 | protected function setUp() |
@@ -55,6 +67,11 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
55 | $this->linkDb = new LinkDB(self::$testDatastore, true, false); | 67 | $this->linkDb = new LinkDB(self::$testDatastore, true, false); |
56 | } | 68 | } |
57 | 69 | ||
70 | public static function tearDownAfterClass() | ||
71 | { | ||
72 | date_default_timezone_set(self::$defaultTimeZone); | ||
73 | } | ||
74 | |||
58 | /** | 75 | /** |
59 | * Attempt to import bookmarks from an empty file | 76 | * Attempt to import bookmarks from an empty file |
60 | */ | 77 | */ |
@@ -98,18 +115,19 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
98 | 115 | ||
99 | $this->assertEquals( | 116 | $this->assertEquals( |
100 | array( | 117 | array( |
101 | 'linkdate' => '20160618_173944', | 118 | 'id' => 0, |
119 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160618_203944'), | ||
102 | 'title' => 'Hg Init a Mercurial tutorial by Joel Spolsky', | 120 | 'title' => 'Hg Init a Mercurial tutorial by Joel Spolsky', |
103 | 'url' => 'http://hginit.com/', | 121 | 'url' => 'http://hginit.com/', |
104 | 'description' => '', | 122 | 'description' => '', |
105 | 'private' => 0, | 123 | 'private' => 0, |
106 | 'tags' => '' | 124 | 'tags' => '', |
125 | 'shorturl' => 'La37cg', | ||
107 | ), | 126 | ), |
108 | $this->linkDb->getLinkFromUrl('http://hginit.com/') | 127 | $this->linkDb->getLinkFromUrl('http://hginit.com/') |
109 | ); | 128 | ); |
110 | } | 129 | } |
111 | 130 | ||
112 | |||
113 | /** | 131 | /** |
114 | * Import bookmarks nested in a folder hierarchy | 132 | * Import bookmarks nested in a folder hierarchy |
115 | */ | 133 | */ |
@@ -126,89 +144,105 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
126 | 144 | ||
127 | $this->assertEquals( | 145 | $this->assertEquals( |
128 | array( | 146 | array( |
129 | 'linkdate' => '20160225_205541', | 147 | 'id' => 0, |
148 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235541'), | ||
130 | 'title' => 'Nested 1', | 149 | 'title' => 'Nested 1', |
131 | 'url' => 'http://nest.ed/1', | 150 | 'url' => 'http://nest.ed/1', |
132 | 'description' => '', | 151 | 'description' => '', |
133 | 'private' => 0, | 152 | 'private' => 0, |
134 | 'tags' => 'tag1 tag2' | 153 | 'tags' => 'tag1 tag2', |
154 | 'shorturl' => 'KyDNKA', | ||
135 | ), | 155 | ), |
136 | $this->linkDb->getLinkFromUrl('http://nest.ed/1') | 156 | $this->linkDb->getLinkFromUrl('http://nest.ed/1') |
137 | ); | 157 | ); |
138 | $this->assertEquals( | 158 | $this->assertEquals( |
139 | array( | 159 | array( |
140 | 'linkdate' => '20160225_205542', | 160 | 'id' => 1, |
161 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235542'), | ||
141 | 'title' => 'Nested 1-1', | 162 | 'title' => 'Nested 1-1', |
142 | 'url' => 'http://nest.ed/1-1', | 163 | 'url' => 'http://nest.ed/1-1', |
143 | 'description' => '', | 164 | 'description' => '', |
144 | 'private' => 0, | 165 | 'private' => 0, |
145 | 'tags' => 'folder1 tag1 tag2' | 166 | 'tags' => 'folder1 tag1 tag2', |
167 | 'shorturl' => 'T2LnXg', | ||
146 | ), | 168 | ), |
147 | $this->linkDb->getLinkFromUrl('http://nest.ed/1-1') | 169 | $this->linkDb->getLinkFromUrl('http://nest.ed/1-1') |
148 | ); | 170 | ); |
149 | $this->assertEquals( | 171 | $this->assertEquals( |
150 | array( | 172 | array( |
151 | 'linkdate' => '20160225_205547', | 173 | 'id' => 2, |
174 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235547'), | ||
152 | 'title' => 'Nested 1-2', | 175 | 'title' => 'Nested 1-2', |
153 | 'url' => 'http://nest.ed/1-2', | 176 | 'url' => 'http://nest.ed/1-2', |
154 | 'description' => '', | 177 | 'description' => '', |
155 | 'private' => 0, | 178 | 'private' => 0, |
156 | 'tags' => 'folder1 tag3 tag4' | 179 | 'tags' => 'folder1 tag3 tag4', |
180 | 'shorturl' => '46SZxA', | ||
157 | ), | 181 | ), |
158 | $this->linkDb->getLinkFromUrl('http://nest.ed/1-2') | 182 | $this->linkDb->getLinkFromUrl('http://nest.ed/1-2') |
159 | ); | 183 | ); |
160 | $this->assertEquals( | 184 | $this->assertEquals( |
161 | array( | 185 | array( |
162 | 'linkdate' => '20160202_172222', | 186 | 'id' => 3, |
187 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160202_202222'), | ||
163 | 'title' => 'Nested 2-1', | 188 | 'title' => 'Nested 2-1', |
164 | 'url' => 'http://nest.ed/2-1', | 189 | 'url' => 'http://nest.ed/2-1', |
165 | 'description' => 'First link of the second section', | 190 | 'description' => 'First link of the second section', |
166 | 'private' => 1, | 191 | 'private' => 1, |
167 | 'tags' => 'folder2' | 192 | 'tags' => 'folder2', |
193 | 'shorturl' => '4UHOSw', | ||
168 | ), | 194 | ), |
169 | $this->linkDb->getLinkFromUrl('http://nest.ed/2-1') | 195 | $this->linkDb->getLinkFromUrl('http://nest.ed/2-1') |
170 | ); | 196 | ); |
171 | $this->assertEquals( | 197 | $this->assertEquals( |
172 | array( | 198 | array( |
173 | 'linkdate' => '20160119_200227', | 199 | 'id' => 4, |
200 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160119_230227'), | ||
174 | 'title' => 'Nested 2-2', | 201 | 'title' => 'Nested 2-2', |
175 | 'url' => 'http://nest.ed/2-2', | 202 | 'url' => 'http://nest.ed/2-2', |
176 | 'description' => 'Second link of the second section', | 203 | 'description' => 'Second link of the second section', |
177 | 'private' => 1, | 204 | 'private' => 1, |
178 | 'tags' => 'folder2' | 205 | 'tags' => 'folder2', |
206 | 'shorturl' => 'yfzwbw', | ||
179 | ), | 207 | ), |
180 | $this->linkDb->getLinkFromUrl('http://nest.ed/2-2') | 208 | $this->linkDb->getLinkFromUrl('http://nest.ed/2-2') |
181 | ); | 209 | ); |
182 | $this->assertEquals( | 210 | $this->assertEquals( |
183 | array( | 211 | array( |
184 | 'linkdate' => '20160202_172223', | 212 | 'id' => 5, |
213 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160202_202222'), | ||
185 | 'title' => 'Nested 3-1', | 214 | 'title' => 'Nested 3-1', |
186 | 'url' => 'http://nest.ed/3-1', | 215 | 'url' => 'http://nest.ed/3-1', |
187 | 'description' => '', | 216 | 'description' => '', |
188 | 'private' => 0, | 217 | 'private' => 0, |
189 | 'tags' => 'folder3 folder3-1 tag3' | 218 | 'tags' => 'folder3 folder3-1 tag3', |
219 | 'shorturl' => 'UwxIUQ', | ||
190 | ), | 220 | ), |
191 | $this->linkDb->getLinkFromUrl('http://nest.ed/3-1') | 221 | $this->linkDb->getLinkFromUrl('http://nest.ed/3-1') |
192 | ); | 222 | ); |
193 | $this->assertEquals( | 223 | $this->assertEquals( |
194 | array( | 224 | array( |
195 | 'linkdate' => '20160119_200228', | 225 | 'id' => 6, |
226 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160119_230227'), | ||
196 | 'title' => 'Nested 3-2', | 227 | 'title' => 'Nested 3-2', |
197 | 'url' => 'http://nest.ed/3-2', | 228 | 'url' => 'http://nest.ed/3-2', |
198 | 'description' => '', | 229 | 'description' => '', |
199 | 'private' => 0, | 230 | 'private' => 0, |
200 | 'tags' => 'folder3 folder3-1' | 231 | 'tags' => 'folder3 folder3-1', |
232 | 'shorturl' => 'p8dyZg', | ||
201 | ), | 233 | ), |
202 | $this->linkDb->getLinkFromUrl('http://nest.ed/3-2') | 234 | $this->linkDb->getLinkFromUrl('http://nest.ed/3-2') |
203 | ); | 235 | ); |
204 | $this->assertEquals( | 236 | $this->assertEquals( |
205 | array( | 237 | array( |
206 | 'linkdate' => '20160229_081541', | 238 | 'id' => 7, |
239 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160229_111541'), | ||
207 | 'title' => 'Nested 2', | 240 | 'title' => 'Nested 2', |
208 | 'url' => 'http://nest.ed/2', | 241 | 'url' => 'http://nest.ed/2', |
209 | 'description' => '', | 242 | 'description' => '', |
210 | 'private' => 0, | 243 | 'private' => 0, |
211 | 'tags' => 'tag4' | 244 | 'tags' => 'tag4', |
245 | 'shorturl' => 'Gt3Uug', | ||
212 | ), | 246 | ), |
213 | $this->linkDb->getLinkFromUrl('http://nest.ed/2') | 247 | $this->linkDb->getLinkFromUrl('http://nest.ed/2') |
214 | ); | 248 | ); |
@@ -227,28 +261,34 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
227 | .' 2 links imported, 0 links overwritten, 0 links skipped.', | 261 | .' 2 links imported, 0 links overwritten, 0 links skipped.', |
228 | NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->pagecache) | 262 | NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->pagecache) |
229 | ); | 263 | ); |
264 | |||
230 | $this->assertEquals(2, count($this->linkDb)); | 265 | $this->assertEquals(2, count($this->linkDb)); |
231 | $this->assertEquals(1, count_private($this->linkDb)); | 266 | $this->assertEquals(1, count_private($this->linkDb)); |
232 | 267 | ||
233 | $this->assertEquals( | 268 | $this->assertEquals( |
234 | array( | 269 | array( |
235 | 'linkdate' => '20001010_105536', | 270 | 'id' => 0, |
271 | // Old link - UTC+4 (note that TZ in the import file is ignored). | ||
272 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20001010_135536'), | ||
236 | 'title' => 'Secret stuff', | 273 | 'title' => 'Secret stuff', |
237 | 'url' => 'https://private.tld', | 274 | 'url' => 'https://private.tld', |
238 | 'description' => "Super-secret stuff you're not supposed to know about", | 275 | 'description' => "Super-secret stuff you're not supposed to know about", |
239 | 'private' => 1, | 276 | 'private' => 1, |
240 | 'tags' => 'private secret' | 277 | 'tags' => 'private secret', |
278 | 'shorturl' => 'EokDtA', | ||
241 | ), | 279 | ), |
242 | $this->linkDb->getLinkFromUrl('https://private.tld') | 280 | $this->linkDb->getLinkFromUrl('https://private.tld') |
243 | ); | 281 | ); |
244 | $this->assertEquals( | 282 | $this->assertEquals( |
245 | array( | 283 | array( |
246 | 'linkdate' => '20160225_205548', | 284 | 'id' => 1, |
285 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235548'), | ||
247 | 'title' => 'Public stuff', | 286 | 'title' => 'Public stuff', |
248 | 'url' => 'http://public.tld', | 287 | 'url' => 'http://public.tld', |
249 | 'description' => '', | 288 | 'description' => '', |
250 | 'private' => 0, | 289 | 'private' => 0, |
251 | 'tags' => 'public hello world' | 290 | 'tags' => 'public hello world', |
291 | 'shorturl' => 'Er9ddA', | ||
252 | ), | 292 | ), |
253 | $this->linkDb->getLinkFromUrl('http://public.tld') | 293 | $this->linkDb->getLinkFromUrl('http://public.tld') |
254 | ); | 294 | ); |
@@ -271,23 +311,28 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
271 | 311 | ||
272 | $this->assertEquals( | 312 | $this->assertEquals( |
273 | array( | 313 | array( |
274 | 'linkdate' => '20001010_105536', | 314 | 'id' => 0, |
315 | // Note that TZ in the import file is ignored. | ||
316 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20001010_135536'), | ||
275 | 'title' => 'Secret stuff', | 317 | 'title' => 'Secret stuff', |
276 | 'url' => 'https://private.tld', | 318 | 'url' => 'https://private.tld', |
277 | 'description' => "Super-secret stuff you're not supposed to know about", | 319 | 'description' => "Super-secret stuff you're not supposed to know about", |
278 | 'private' => 1, | 320 | 'private' => 1, |
279 | 'tags' => 'private secret' | 321 | 'tags' => 'private secret', |
322 | 'shorturl' => 'EokDtA', | ||
280 | ), | 323 | ), |
281 | $this->linkDb->getLinkFromUrl('https://private.tld') | 324 | $this->linkDb->getLinkFromUrl('https://private.tld') |
282 | ); | 325 | ); |
283 | $this->assertEquals( | 326 | $this->assertEquals( |
284 | array( | 327 | array( |
285 | 'linkdate' => '20160225_205548', | 328 | 'id' => 1, |
329 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235548'), | ||
286 | 'title' => 'Public stuff', | 330 | 'title' => 'Public stuff', |
287 | 'url' => 'http://public.tld', | 331 | 'url' => 'http://public.tld', |
288 | 'description' => '', | 332 | 'description' => '', |
289 | 'private' => 0, | 333 | 'private' => 0, |
290 | 'tags' => 'public hello world' | 334 | 'tags' => 'public hello world', |
335 | 'shorturl' => 'Er9ddA', | ||
291 | ), | 336 | ), |
292 | $this->linkDb->getLinkFromUrl('http://public.tld') | 337 | $this->linkDb->getLinkFromUrl('http://public.tld') |
293 | ); | 338 | ); |
@@ -309,11 +354,11 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
309 | $this->assertEquals(0, count_private($this->linkDb)); | 354 | $this->assertEquals(0, count_private($this->linkDb)); |
310 | $this->assertEquals( | 355 | $this->assertEquals( |
311 | 0, | 356 | 0, |
312 | $this->linkDb['20001010_105536']['private'] | 357 | $this->linkDb[0]['private'] |
313 | ); | 358 | ); |
314 | $this->assertEquals( | 359 | $this->assertEquals( |
315 | 0, | 360 | 0, |
316 | $this->linkDb['20160225_205548']['private'] | 361 | $this->linkDb[1]['private'] |
317 | ); | 362 | ); |
318 | } | 363 | } |
319 | 364 | ||
@@ -333,11 +378,11 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
333 | $this->assertEquals(2, count_private($this->linkDb)); | 378 | $this->assertEquals(2, count_private($this->linkDb)); |
334 | $this->assertEquals( | 379 | $this->assertEquals( |
335 | 1, | 380 | 1, |
336 | $this->linkDb['20001010_105536']['private'] | 381 | $this->linkDb['0']['private'] |
337 | ); | 382 | ); |
338 | $this->assertEquals( | 383 | $this->assertEquals( |
339 | 1, | 384 | 1, |
340 | $this->linkDb['20160225_205548']['private'] | 385 | $this->linkDb['1']['private'] |
341 | ); | 386 | ); |
342 | } | 387 | } |
343 | 388 | ||
@@ -359,13 +404,12 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
359 | $this->assertEquals(2, count_private($this->linkDb)); | 404 | $this->assertEquals(2, count_private($this->linkDb)); |
360 | $this->assertEquals( | 405 | $this->assertEquals( |
361 | 1, | 406 | 1, |
362 | $this->linkDb['20001010_105536']['private'] | 407 | $this->linkDb[0]['private'] |
363 | ); | 408 | ); |
364 | $this->assertEquals( | 409 | $this->assertEquals( |
365 | 1, | 410 | 1, |
366 | $this->linkDb['20160225_205548']['private'] | 411 | $this->linkDb[1]['private'] |
367 | ); | 412 | ); |
368 | |||
369 | // re-import as public, enable overwriting | 413 | // re-import as public, enable overwriting |
370 | $post = array( | 414 | $post = array( |
371 | 'privacy' => 'public', | 415 | 'privacy' => 'public', |
@@ -380,11 +424,11 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
380 | $this->assertEquals(0, count_private($this->linkDb)); | 424 | $this->assertEquals(0, count_private($this->linkDb)); |
381 | $this->assertEquals( | 425 | $this->assertEquals( |
382 | 0, | 426 | 0, |
383 | $this->linkDb['20001010_105536']['private'] | 427 | $this->linkDb[0]['private'] |
384 | ); | 428 | ); |
385 | $this->assertEquals( | 429 | $this->assertEquals( |
386 | 0, | 430 | 0, |
387 | $this->linkDb['20160225_205548']['private'] | 431 | $this->linkDb[1]['private'] |
388 | ); | 432 | ); |
389 | } | 433 | } |
390 | 434 | ||
@@ -406,11 +450,11 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
406 | $this->assertEquals(0, count_private($this->linkDb)); | 450 | $this->assertEquals(0, count_private($this->linkDb)); |
407 | $this->assertEquals( | 451 | $this->assertEquals( |
408 | 0, | 452 | 0, |
409 | $this->linkDb['20001010_105536']['private'] | 453 | $this->linkDb['0']['private'] |
410 | ); | 454 | ); |
411 | $this->assertEquals( | 455 | $this->assertEquals( |
412 | 0, | 456 | 0, |
413 | $this->linkDb['20160225_205548']['private'] | 457 | $this->linkDb['1']['private'] |
414 | ); | 458 | ); |
415 | 459 | ||
416 | // re-import as private, enable overwriting | 460 | // re-import as private, enable overwriting |
@@ -427,11 +471,11 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
427 | $this->assertEquals(2, count_private($this->linkDb)); | 471 | $this->assertEquals(2, count_private($this->linkDb)); |
428 | $this->assertEquals( | 472 | $this->assertEquals( |
429 | 1, | 473 | 1, |
430 | $this->linkDb['20001010_105536']['private'] | 474 | $this->linkDb['0']['private'] |
431 | ); | 475 | ); |
432 | $this->assertEquals( | 476 | $this->assertEquals( |
433 | 1, | 477 | 1, |
434 | $this->linkDb['20160225_205548']['private'] | 478 | $this->linkDb['1']['private'] |
435 | ); | 479 | ); |
436 | } | 480 | } |
437 | 481 | ||
@@ -480,11 +524,11 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
480 | $this->assertEquals(0, count_private($this->linkDb)); | 524 | $this->assertEquals(0, count_private($this->linkDb)); |
481 | $this->assertEquals( | 525 | $this->assertEquals( |
482 | 'tag1 tag2 tag3 private secret', | 526 | 'tag1 tag2 tag3 private secret', |
483 | $this->linkDb['20001010_105536']['tags'] | 527 | $this->linkDb['0']['tags'] |
484 | ); | 528 | ); |
485 | $this->assertEquals( | 529 | $this->assertEquals( |
486 | 'tag1 tag2 tag3 public hello world', | 530 | 'tag1 tag2 tag3 public hello world', |
487 | $this->linkDb['20160225_205548']['tags'] | 531 | $this->linkDb['1']['tags'] |
488 | ); | 532 | ); |
489 | } | 533 | } |
490 | 534 | ||
@@ -507,16 +551,16 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
507 | $this->assertEquals(0, count_private($this->linkDb)); | 551 | $this->assertEquals(0, count_private($this->linkDb)); |
508 | $this->assertEquals( | 552 | $this->assertEquals( |
509 | 'tag1& tag2 "tag3" private secret', | 553 | 'tag1& tag2 "tag3" private secret', |
510 | $this->linkDb['20001010_105536']['tags'] | 554 | $this->linkDb['0']['tags'] |
511 | ); | 555 | ); |
512 | $this->assertEquals( | 556 | $this->assertEquals( |
513 | 'tag1& tag2 "tag3" public hello world', | 557 | 'tag1& tag2 "tag3" public hello world', |
514 | $this->linkDb['20160225_205548']['tags'] | 558 | $this->linkDb['1']['tags'] |
515 | ); | 559 | ); |
516 | } | 560 | } |
517 | 561 | ||
518 | /** | 562 | /** |
519 | * Ensure each imported bookmark has a unique linkdate | 563 | * Ensure each imported bookmark has a unique id |
520 | * | 564 | * |
521 | * See https://github.com/shaarli/Shaarli/issues/351 | 565 | * See https://github.com/shaarli/Shaarli/issues/351 |
522 | */ | 566 | */ |
@@ -531,16 +575,16 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
531 | $this->assertEquals(3, count($this->linkDb)); | 575 | $this->assertEquals(3, count($this->linkDb)); |
532 | $this->assertEquals(0, count_private($this->linkDb)); | 576 | $this->assertEquals(0, count_private($this->linkDb)); |
533 | $this->assertEquals( | 577 | $this->assertEquals( |
534 | '20160225_205548', | 578 | 0, |
535 | $this->linkDb['20160225_205548']['linkdate'] | 579 | $this->linkDb[0]['id'] |
536 | ); | 580 | ); |
537 | $this->assertEquals( | 581 | $this->assertEquals( |
538 | '20160225_205549', | 582 | 1, |
539 | $this->linkDb['20160225_205549']['linkdate'] | 583 | $this->linkDb[1]['id'] |
540 | ); | 584 | ); |
541 | $this->assertEquals( | 585 | $this->assertEquals( |
542 | '20160225_205550', | 586 | 2, |
543 | $this->linkDb['20160225_205550']['linkdate'] | 587 | $this->linkDb[2]['id'] |
544 | ); | 588 | ); |
545 | } | 589 | } |
546 | } | 590 | } |
diff --git a/tests/Updater/UpdaterTest.php b/tests/Updater/UpdaterTest.php index 0d0ad922..4948fe52 100644 --- a/tests/Updater/UpdaterTest.php +++ b/tests/Updater/UpdaterTest.php | |||
@@ -214,6 +214,7 @@ $GLOBALS[\'privateLinkByDefault\'] = true;'; | |||
214 | $refDB = new ReferenceLinkDB(); | 214 | $refDB = new ReferenceLinkDB(); |
215 | $refDB->write(self::$testDatastore); | 215 | $refDB->write(self::$testDatastore); |
216 | $linkDB = new LinkDB(self::$testDatastore, true, false); | 216 | $linkDB = new LinkDB(self::$testDatastore, true, false); |
217 | |||
217 | $this->assertEmpty($linkDB->filterSearch(array('searchtags' => 'exclude'))); | 218 | $this->assertEmpty($linkDB->filterSearch(array('searchtags' => 'exclude'))); |
218 | $updater = new Updater(array(), $linkDB, $this->conf, true); | 219 | $updater = new Updater(array(), $linkDB, $this->conf, true); |
219 | $updater->updateMethodRenameDashTags(); | 220 | $updater->updateMethodRenameDashTags(); |
@@ -287,4 +288,101 @@ $GLOBALS[\'privateLinkByDefault\'] = true;'; | |||
287 | $this->assertEquals(escape($redirectorUrl), $this->conf->get('redirector.url')); | 288 | $this->assertEquals(escape($redirectorUrl), $this->conf->get('redirector.url')); |
288 | unlink($sandbox .'.json.php'); | 289 | unlink($sandbox .'.json.php'); |
289 | } | 290 | } |
291 | |||
292 | /** | ||
293 | * Test updateMethodDatastoreIds(). | ||
294 | */ | ||
295 | public function testDatastoreIds() | ||
296 | { | ||
297 | $links = array( | ||
298 | '20121206_182539' => array( | ||
299 | 'linkdate' => '20121206_182539', | ||
300 | 'title' => 'Geek and Poke', | ||
301 | 'url' => 'http://geek-and-poke.com/', | ||
302 | 'description' => 'desc', | ||
303 | 'tags' => 'dev cartoon tag1 tag2 tag3 tag4 ', | ||
304 | 'updated' => '20121206_190301', | ||
305 | 'private' => false, | ||
306 | ), | ||
307 | '20121206_172539' => array( | ||
308 | 'linkdate' => '20121206_172539', | ||
309 | 'title' => 'UserFriendly - Samba', | ||
310 | 'url' => 'http://ars.userfriendly.org/cartoons/?id=20010306', | ||
311 | 'description' => '', | ||
312 | 'tags' => 'samba cartoon web', | ||
313 | 'private' => false, | ||
314 | ), | ||
315 | '20121206_142300' => array( | ||
316 | 'linkdate' => '20121206_142300', | ||
317 | 'title' => 'UserFriendly - Web Designer', | ||
318 | 'url' => 'http://ars.userfriendly.org/cartoons/?id=20121206', | ||
319 | 'description' => 'Naming conventions... #private', | ||
320 | 'tags' => 'samba cartoon web', | ||
321 | 'private' => true, | ||
322 | ), | ||
323 | ); | ||
324 | $refDB = new ReferenceLinkDB(); | ||
325 | $refDB->setLinks($links); | ||
326 | $refDB->write(self::$testDatastore); | ||
327 | $linkDB = new LinkDB(self::$testDatastore, true, false); | ||
328 | |||
329 | $checksum = hash_file('sha1', self::$testDatastore); | ||
330 | |||
331 | $this->conf->set('resource.data_dir', 'sandbox'); | ||
332 | $this->conf->set('resource.datastore', self::$testDatastore); | ||
333 | |||
334 | $updater = new Updater(array(), $linkDB, $this->conf, true); | ||
335 | $this->assertTrue($updater->updateMethodDatastoreIds()); | ||
336 | |||
337 | $linkDB = new LinkDB(self::$testDatastore, true, false); | ||
338 | |||
339 | $backup = glob($this->conf->get('resource.data_dir') . '/datastore.'. date('YmdH') .'*.php'); | ||
340 | $backup = $backup[0]; | ||
341 | |||
342 | $this->assertFileExists($backup); | ||
343 | $this->assertEquals($checksum, hash_file('sha1', $backup)); | ||
344 | unlink($backup); | ||
345 | |||
346 | $this->assertEquals(3, count($linkDB)); | ||
347 | $this->assertTrue(isset($linkDB[0])); | ||
348 | $this->assertFalse(isset($linkDB[0]['linkdate'])); | ||
349 | $this->assertEquals(0, $linkDB[0]['id']); | ||
350 | $this->assertEquals('UserFriendly - Web Designer', $linkDB[0]['title']); | ||
351 | $this->assertEquals('http://ars.userfriendly.org/cartoons/?id=20121206', $linkDB[0]['url']); | ||
352 | $this->assertEquals('Naming conventions... #private', $linkDB[0]['description']); | ||
353 | $this->assertEquals('samba cartoon web', $linkDB[0]['tags']); | ||
354 | $this->assertTrue($linkDB[0]['private']); | ||
355 | $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_142300'), $linkDB[0]['created']); | ||
356 | |||
357 | $this->assertTrue(isset($linkDB[1])); | ||
358 | $this->assertFalse(isset($linkDB[1]['linkdate'])); | ||
359 | $this->assertEquals(1, $linkDB[1]['id']); | ||
360 | $this->assertEquals('UserFriendly - Samba', $linkDB[1]['title']); | ||
361 | $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_172539'), $linkDB[1]['created']); | ||
362 | |||
363 | $this->assertTrue(isset($linkDB[2])); | ||
364 | $this->assertFalse(isset($linkDB[2]['linkdate'])); | ||
365 | $this->assertEquals(2, $linkDB[2]['id']); | ||
366 | $this->assertEquals('Geek and Poke', $linkDB[2]['title']); | ||
367 | $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_182539'), $linkDB[2]['created']); | ||
368 | $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_190301'), $linkDB[2]['updated']); | ||
369 | } | ||
370 | |||
371 | /** | ||
372 | * Test updateMethodDatastoreIds() with the update already applied: nothing to do. | ||
373 | */ | ||
374 | public function testDatastoreIdsNothingToDo() | ||
375 | { | ||
376 | $refDB = new ReferenceLinkDB(); | ||
377 | $refDB->write(self::$testDatastore); | ||
378 | $linkDB = new LinkDB(self::$testDatastore, true, false); | ||
379 | |||
380 | $this->conf->set('resource.data_dir', 'sandbox'); | ||
381 | $this->conf->set('resource.datastore', self::$testDatastore); | ||
382 | |||
383 | $checksum = hash_file('sha1', self::$testDatastore); | ||
384 | $updater = new Updater(array(), $linkDB, $this->conf, true); | ||
385 | $this->assertTrue($updater->updateMethodDatastoreIds()); | ||
386 | $this->assertEquals($checksum, hash_file('sha1', self::$testDatastore)); | ||
387 | } | ||
290 | } | 388 | } |
diff --git a/tests/plugins/PluginIssoTest.php b/tests/plugins/PluginIssoTest.php index 1f545c7d..6b7904dd 100644 --- a/tests/plugins/PluginIssoTest.php +++ b/tests/plugins/PluginIssoTest.php | |||
@@ -47,12 +47,14 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase | |||
47 | $conf->set('plugins.ISSO_SERVER', 'value'); | 47 | $conf->set('plugins.ISSO_SERVER', 'value'); |
48 | 48 | ||
49 | $str = 'http://randomstr.com/test'; | 49 | $str = 'http://randomstr.com/test'; |
50 | $date = '20161118_100001'; | ||
50 | $data = array( | 51 | $data = array( |
51 | 'title' => $str, | 52 | 'title' => $str, |
52 | 'links' => array( | 53 | 'links' => array( |
53 | array( | 54 | array( |
55 | 'id' => 12, | ||
54 | 'url' => $str, | 56 | 'url' => $str, |
55 | 'linkdate' => 'abc', | 57 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date), |
56 | ) | 58 | ) |
57 | ) | 59 | ) |
58 | ); | 60 | ); |
@@ -65,7 +67,14 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase | |||
65 | 67 | ||
66 | // plugin data | 68 | // plugin data |
67 | $this->assertEquals(1, count($data['plugin_end_zone'])); | 69 | $this->assertEquals(1, count($data['plugin_end_zone'])); |
68 | $this->assertNotFalse(strpos($data['plugin_end_zone'][0], 'abc')); | 70 | $this->assertNotFalse(strpos( |
71 | $data['plugin_end_zone'][0], | ||
72 | 'data-isso-id="'. $data['links'][0]['id'] .'"' | ||
73 | )); | ||
74 | $this->assertNotFalse(strpos( | ||
75 | $data['plugin_end_zone'][0], | ||
76 | 'data-title="'. $data['links'][0]['id'] .'"' | ||
77 | )); | ||
69 | $this->assertNotFalse(strpos($data['plugin_end_zone'][0], 'embed.min.js')); | 78 | $this->assertNotFalse(strpos($data['plugin_end_zone'][0], 'embed.min.js')); |
70 | } | 79 | } |
71 | 80 | ||
@@ -78,16 +87,20 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase | |||
78 | $conf->set('plugins.ISSO_SERVER', 'value'); | 87 | $conf->set('plugins.ISSO_SERVER', 'value'); |
79 | 88 | ||
80 | $str = 'http://randomstr.com/test'; | 89 | $str = 'http://randomstr.com/test'; |
90 | $date1 = '20161118_100001'; | ||
91 | $date2 = '20161118_100002'; | ||
81 | $data = array( | 92 | $data = array( |
82 | 'title' => $str, | 93 | 'title' => $str, |
83 | 'links' => array( | 94 | 'links' => array( |
84 | array( | 95 | array( |
96 | 'id' => 12, | ||
85 | 'url' => $str, | 97 | 'url' => $str, |
86 | 'linkdate' => 'abc', | 98 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date1), |
87 | ), | 99 | ), |
88 | array( | 100 | array( |
101 | 'id' => 13, | ||
89 | 'url' => $str . '2', | 102 | 'url' => $str . '2', |
90 | 'linkdate' => 'abc2', | 103 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date2), |
91 | ), | 104 | ), |
92 | ) | 105 | ) |
93 | ); | 106 | ); |
@@ -106,12 +119,14 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase | |||
106 | $conf->set('plugins.ISSO_SERVER', 'value'); | 119 | $conf->set('plugins.ISSO_SERVER', 'value'); |
107 | 120 | ||
108 | $str = 'http://randomstr.com/test'; | 121 | $str = 'http://randomstr.com/test'; |
122 | $date = '20161118_100001'; | ||
109 | $data = array( | 123 | $data = array( |
110 | 'title' => $str, | 124 | 'title' => $str, |
111 | 'links' => array( | 125 | 'links' => array( |
112 | array( | 126 | array( |
127 | 'id' => 12, | ||
113 | 'url' => $str, | 128 | 'url' => $str, |
114 | 'linkdate' => 'abc', | 129 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date), |
115 | ) | 130 | ) |
116 | ), | 131 | ), |
117 | 'search_term' => $str | 132 | 'search_term' => $str |
diff --git a/tests/plugins/PluginMarkdownTest.php b/tests/plugins/PluginMarkdownTest.php index 12bdda24..4a67b2dc 100644 --- a/tests/plugins/PluginMarkdownTest.php +++ b/tests/plugins/PluginMarkdownTest.php | |||
@@ -8,8 +8,8 @@ require_once 'application/Utils.php'; | |||
8 | require_once 'plugins/markdown/markdown.php'; | 8 | require_once 'plugins/markdown/markdown.php'; |
9 | 9 | ||
10 | /** | 10 | /** |
11 | * Class PlugQrcodeTest | 11 | * Class PluginMarkdownTest |
12 | * Unit test for the QR-Code plugin | 12 | * Unit test for the Markdown plugin |
13 | */ | 13 | */ |
14 | class PluginMarkdownTest extends PHPUnit_Framework_TestCase | 14 | class PluginMarkdownTest extends PHPUnit_Framework_TestCase |
15 | { | 15 | { |
@@ -130,8 +130,11 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase | |||
130 | )) | 130 | )) |
131 | ); | 131 | ); |
132 | 132 | ||
133 | $data = hook_markdown_render_linklist($data); | 133 | $processed = hook_markdown_render_linklist($data); |
134 | $this->assertEquals($str, $data['links'][0]['description']); | 134 | $this->assertEquals($str, $processed['links'][0]['description']); |
135 | |||
136 | $processed = hook_markdown_render_feed($data); | ||
137 | $this->assertEquals($str, $processed['links'][0]['description']); | ||
135 | 138 | ||
136 | $data = array( | 139 | $data = array( |
137 | // Columns data | 140 | // Columns data |
@@ -153,6 +156,37 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase | |||
153 | } | 156 | } |
154 | 157 | ||
155 | /** | 158 | /** |
159 | * Test that a close value to nomarkdown is not understand as nomarkdown (previous value `.nomarkdown`). | ||
160 | */ | ||
161 | function testNoMarkdownNotExcactlyMatching() | ||
162 | { | ||
163 | $str = 'All _work_ and `no play` makes Jack a *dull* boy.'; | ||
164 | $data = array( | ||
165 | 'links' => array(array( | ||
166 | 'description' => $str, | ||
167 | 'tags' => '.' . NO_MD_TAG, | ||
168 | 'taglist' => array('.'. NO_MD_TAG), | ||
169 | )) | ||
170 | ); | ||
171 | |||
172 | $data = hook_markdown_render_feed($data); | ||
173 | $this->assertContains('<em>', $data['links'][0]['description']); | ||
174 | } | ||
175 | |||
176 | /** | ||
177 | * Test hashtag links processed with markdown. | ||
178 | */ | ||
179 | function testMarkdownHashtagLinks() | ||
180 | { | ||
181 | $md = file_get_contents('tests/plugins/resources/markdown.md'); | ||
182 | $md = format_description($md); | ||
183 | $html = file_get_contents('tests/plugins/resources/markdown.html'); | ||
184 | |||
185 | $data = process_markdown($md); | ||
186 | $this->assertEquals($html, $data); | ||
187 | } | ||
188 | |||
189 | /** | ||
156 | * Test hashtag links processed with markdown. | 190 | * Test hashtag links processed with markdown. |
157 | */ | 191 | */ |
158 | function testMarkdownHashtagLinks() | 192 | function testMarkdownHashtagLinks() |
diff --git a/tests/utils/ReferenceLinkDB.php b/tests/utils/ReferenceLinkDB.php index abca4656..36d58c68 100644 --- a/tests/utils/ReferenceLinkDB.php +++ b/tests/utils/ReferenceLinkDB.php | |||
@@ -4,7 +4,7 @@ | |||
4 | */ | 4 | */ |
5 | class ReferenceLinkDB | 5 | class ReferenceLinkDB |
6 | { | 6 | { |
7 | public static $NB_LINKS_TOTAL = 7; | 7 | public static $NB_LINKS_TOTAL = 8; |
8 | 8 | ||
9 | private $_links = array(); | 9 | private $_links = array(); |
10 | private $_publicCount = 0; | 10 | private $_publicCount = 0; |
@@ -16,66 +16,87 @@ class ReferenceLinkDB | |||
16 | public function __construct() | 16 | public function __construct() |
17 | { | 17 | { |
18 | $this->addLink( | 18 | $this->addLink( |
19 | 41, | ||
19 | 'Link title: @website', | 20 | 'Link title: @website', |
20 | '?WDWyig', | 21 | '?WDWyig', |
21 | 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag', | 22 | 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag', |
22 | 0, | 23 | 0, |
23 | '20150310_114651', | 24 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), |
24 | 'sTuff' | 25 | 'sTuff', |
26 | null, | ||
27 | 'WDWyig' | ||
25 | ); | 28 | ); |
26 | 29 | ||
27 | $this->addLink( | 30 | $this->addLink( |
31 | 42, | ||
32 | 'Note: I have a big ID but an old date', | ||
33 | '?WDWyig', | ||
34 | 'Used to test links reordering.', | ||
35 | 0, | ||
36 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20100310_101010'), | ||
37 | 'ut' | ||
38 | ); | ||
39 | |||
40 | $this->addLink( | ||
41 | 8, | ||
28 | 'Free as in Freedom 2.0 @website', | 42 | 'Free as in Freedom 2.0 @website', |
29 | 'https://static.fsf.org/nosvn/faif-2.0.pdf', | 43 | 'https://static.fsf.org/nosvn/faif-2.0.pdf', |
30 | 'Richard Stallman and the Free Software Revolution. Read this. #hashtag', | 44 | 'Richard Stallman and the Free Software Revolution. Read this. #hashtag', |
31 | 0, | 45 | 0, |
32 | '20150310_114633', | 46 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114633'), |
33 | 'free gnu software stallman -exclude stuff hashtag', | 47 | 'free gnu software stallman -exclude stuff hashtag', |
34 | '20160803_093033' | 48 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160803_093033') |
35 | ); | 49 | ); |
36 | 50 | ||
37 | $this->addLink( | 51 | $this->addLink( |
52 | 7, | ||
38 | 'MediaGoblin', | 53 | 'MediaGoblin', |
39 | 'http://mediagoblin.org/', | 54 | 'http://mediagoblin.org/', |
40 | 'A free software media publishing platform #hashtagOther', | 55 | 'A free software media publishing platform #hashtagOther', |
41 | 0, | 56 | 0, |
42 | '20130614_184135', | 57 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20130614_184135'), |
43 | 'gnu media web .hidden hashtag' | 58 | 'gnu media web .hidden hashtag', |
59 | null, | ||
60 | 'IuWvgA' | ||
44 | ); | 61 | ); |
45 | 62 | ||
46 | $this->addLink( | 63 | $this->addLink( |
64 | 6, | ||
47 | 'w3c-markup-validator', | 65 | 'w3c-markup-validator', |
48 | 'https://dvcs.w3.org/hg/markup-validator/summary', | 66 | 'https://dvcs.w3.org/hg/markup-validator/summary', |
49 | 'Mercurial repository for the W3C Validator #private', | 67 | 'Mercurial repository for the W3C Validator #private', |
50 | 1, | 68 | 1, |
51 | '20141125_084734', | 69 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20141125_084734'), |
52 | 'css html w3c web Mercurial' | 70 | 'css html w3c web Mercurial' |
53 | ); | 71 | ); |
54 | 72 | ||
55 | $this->addLink( | 73 | $this->addLink( |
74 | 4, | ||
56 | 'UserFriendly - Web Designer', | 75 | 'UserFriendly - Web Designer', |
57 | 'http://ars.userfriendly.org/cartoons/?id=20121206', | 76 | 'http://ars.userfriendly.org/cartoons/?id=20121206', |
58 | 'Naming conventions... #private', | 77 | 'Naming conventions... #private', |
59 | 0, | 78 | 0, |
60 | '20121206_142300', | 79 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_142300'), |
61 | 'dev cartoon web' | 80 | 'dev cartoon web' |
62 | ); | 81 | ); |
63 | 82 | ||
64 | $this->addLink( | 83 | $this->addLink( |
84 | 1, | ||
65 | 'UserFriendly - Samba', | 85 | 'UserFriendly - Samba', |
66 | 'http://ars.userfriendly.org/cartoons/?id=20010306', | 86 | 'http://ars.userfriendly.org/cartoons/?id=20010306', |
67 | 'Tropical printing', | 87 | 'Tropical printing', |
68 | 0, | 88 | 0, |
69 | '20121206_172539', | 89 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_172539'), |
70 | 'samba cartoon web' | 90 | 'samba cartoon web' |
71 | ); | 91 | ); |
72 | 92 | ||
73 | $this->addLink( | 93 | $this->addLink( |
94 | 0, | ||
74 | 'Geek and Poke', | 95 | 'Geek and Poke', |
75 | 'http://geek-and-poke.com/', | 96 | 'http://geek-and-poke.com/', |
76 | '', | 97 | '', |
77 | 1, | 98 | 1, |
78 | '20121206_182539', | 99 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_182539'), |
79 | 'dev cartoon tag1 tag2 tag3 tag4 ' | 100 | 'dev cartoon tag1 tag2 tag3 tag4 ' |
80 | ); | 101 | ); |
81 | } | 102 | } |
@@ -83,18 +104,20 @@ class ReferenceLinkDB | |||
83 | /** | 104 | /** |
84 | * Adds a new link | 105 | * Adds a new link |
85 | */ | 106 | */ |
86 | protected function addLink($title, $url, $description, $private, $date, $tags, $updated = '') | 107 | protected function addLink($id, $title, $url, $description, $private, $date, $tags, $updated = '', $shorturl = '') |
87 | { | 108 | { |
88 | $link = array( | 109 | $link = array( |
110 | 'id' => $id, | ||
89 | 'title' => $title, | 111 | 'title' => $title, |
90 | 'url' => $url, | 112 | 'url' => $url, |
91 | 'description' => $description, | 113 | 'description' => $description, |
92 | 'private' => $private, | 114 | 'private' => $private, |
93 | 'linkdate' => $date, | ||
94 | 'tags' => $tags, | 115 | 'tags' => $tags, |
116 | 'created' => $date, | ||
95 | 'updated' => $updated, | 117 | 'updated' => $updated, |
118 | 'shorturl' => $shorturl ? $shorturl : smallHash($date->format(LinkDB::LINK_DATE_FORMAT) . $id), | ||
96 | ); | 119 | ); |
97 | $this->_links[$date] = $link; | 120 | $this->_links[$id] = $link; |
98 | 121 | ||
99 | if ($private) { | 122 | if ($private) { |
100 | $this->_privateCount++; | 123 | $this->_privateCount++; |
@@ -142,4 +165,14 @@ class ReferenceLinkDB | |||
142 | { | 165 | { |
143 | return $this->_links; | 166 | return $this->_links; |
144 | } | 167 | } |
168 | |||
169 | /** | ||
170 | * Setter to override link creation. | ||
171 | * | ||
172 | * @param array $links List of links. | ||
173 | */ | ||
174 | public function setLinks($links) | ||
175 | { | ||
176 | $this->_links = $links; | ||
177 | } | ||
145 | } | 178 | } |
diff --git a/tmp/.htaccess b/tmp/.htaccess index b584d98c..f601c1ee 100644 --- a/tmp/.htaccess +++ b/tmp/.htaccess | |||
@@ -1,2 +1,13 @@ | |||
1 | Allow from none | 1 | <IfModule version_module> |
2 | Deny from all | 2 | <IfVersion >= 2.4> |
3 | Require all denied | ||
4 | </IfVersion> | ||
5 | <IfVersion < 2.4> | ||
6 | Allow from none | ||
7 | Deny from all | ||
8 | </IfVersion> | ||
9 | </IfModule> | ||
10 | |||
11 | <IfModule !version_module> | ||
12 | Require all denied | ||
13 | </IfModule> | ||
diff --git a/tpl/daily.html b/tpl/daily.html index b82ad483..eba0af3b 100644 --- a/tpl/daily.html +++ b/tpl/daily.html | |||
@@ -49,13 +49,13 @@ | |||
49 | {$link=$value} | 49 | {$link=$value} |
50 | <div class="dailyEntry"> | 50 | <div class="dailyEntry"> |
51 | <div class="dailyEntryPermalink"> | 51 | <div class="dailyEntryPermalink"> |
52 | <a href="?{$link.linkdate|smallHash}"> | 52 | <a href="?{$value.shorturl}"> |
53 | <img src="../images/squiggle2.png" width="25" height="26" title="permalink" alt="permalink"> | 53 | <img src="../images/squiggle2.png" width="25" height="26" title="permalink" alt="permalink"> |
54 | </a> | 54 | </a> |
55 | </div> | 55 | </div> |
56 | {if="!$hide_timestamps || isLoggedIn()"} | 56 | {if="!$hide_timestamps || isLoggedIn()"} |
57 | <div class="dailyEntryLinkdate"> | 57 | <div class="dailyEntryLinkdate"> |
58 | <a href="?{$link.linkdate|smallHash}">{function="strftime('%c', $link.timestamp)"}</a> | 58 | <a href="?{$value.shorturl}">{function="strftime('%c', $link.timestamp)"}</a> |
59 | </div> | 59 | </div> |
60 | {/if} | 60 | {/if} |
61 | {if="$link.tags"} | 61 | {if="$link.tags"} |
diff --git a/tpl/editlink.html b/tpl/editlink.html index 441b5302..870cc168 100644 --- a/tpl/editlink.html +++ b/tpl/editlink.html | |||
@@ -8,13 +8,18 @@ | |||
8 | {elseif="$link.description==''"}onload="document.linkform.lf_description.focus();" | 8 | {elseif="$link.description==''"}onload="document.linkform.lf_description.focus();" |
9 | {else}onload="document.linkform.lf_tags.focus();"{/if} > | 9 | {else}onload="document.linkform.lf_tags.focus();"{/if} > |
10 | <div id="pageheader"> | 10 | <div id="pageheader"> |
11 | {if="$source !== 'firefoxsocialapi'"} | 11 | {if="$source !== 'firefoxsocialapi'"} |
12 | {include="page.header"} | 12 | {include="page.header"} |
13 | {/if} | 13 | {else} |
14 | <div id="editlinkform"> | 14 | <div id="shaarli_title"><a href="{$titleLink}">{$shaarlititle}</a></div> |
15 | <form method="post" name="linkform"> | 15 | {/if} |
16 | <input type="hidden" name="lf_linkdate" value="{$link.linkdate}"> | 16 | <div id="editlinkform"> |
17 | <label for="lf_url"><i>URL</i></label><br><input type="text" name="lf_url" id="lf_url" value="{$link.url}" class="lf_input"><br> | 17 | <form method="post" name="linkform"> |
18 | <input type="hidden" name="lf_linkdate" value="{$link.linkdate}"> | ||
19 | {if="isset($link.id)"} | ||
20 | <input type="hidden" name="lf_id" value="{$link.id}"> | ||
21 | {/if} | ||
22 | <label for="lf_url"><i>URL</i></label><br><input type="text" name="lf_url" id="lf_url" value="{$link.url}" class="lf_input"><br> | ||
18 | <label for="lf_title"><i>Title</i></label><br><input type="text" name="lf_title" id="lf_title" value="{$link.title}" class="lf_input"><br> | 23 | <label for="lf_title"><i>Title</i></label><br><input type="text" name="lf_title" id="lf_title" value="{$link.title}" class="lf_input"><br> |
19 | <label for="lf_description"><i>Description</i></label><br><textarea name="lf_description" id="lf_description" rows="4" cols="25">{$link.description}</textarea><br> | 24 | <label for="lf_description"><i>Description</i></label><br><textarea name="lf_description" id="lf_description" rows="4" cols="25">{$link.description}</textarea><br> |
20 | <label for="lf_tags"><i>Tags</i></label><br> | 25 | <label for="lf_tags"><i>Tags</i></label><br> |
@@ -25,20 +30,20 @@ | |||
25 | {$value} | 30 | {$value} |
26 | {/loop} | 31 | {/loop} |
27 | 32 | ||
28 | {if="($link_is_new && $default_private_links) || $link.private == true"} | 33 | {if="($link_is_new && $default_private_links) || $link.private == true"} |
29 | <input type="checkbox" checked="checked" name="lf_private" id="lf_private"> | 34 | <input type="checkbox" checked="checked" name="lf_private" id="lf_private"> |
30 | <label for="lf_private"><i>Private</i></label><br> | 35 | <label for="lf_private"><i>Private</i></label><br> |
31 | {else} | 36 | {else} |
32 | <input type="checkbox" name="lf_private" id="lf_private"> | 37 | <input type="checkbox" name="lf_private" id="lf_private"> |
33 | <label for="lf_private"><i>Private</i></label><br> | 38 | <label for="lf_private"><i>Private</i></label><br> |
34 | {/if} | 39 | {/if} |
35 | <input type="submit" value="Save" name="save_edit" class="bigbutton"> | 40 | <input type="submit" value="Save" name="save_edit" class="bigbutton"> |
36 | <input type="submit" value="Cancel" name="cancel_edit" class="bigbutton"> | 41 | <input type="submit" value="Cancel" name="cancel_edit" class="bigbutton"> |
37 | {if="!$link_is_new"}<input type="submit" value="Delete" name="delete_link" class="bigbutton delete" onClick="return confirmDeleteLink();">{/if} | 42 | {if="!$link_is_new"}<input type="submit" value="Delete" name="delete_link" class="bigbutton delete" onClick="return confirmDeleteLink();">{/if} |
38 | <input type="hidden" name="token" value="{$token}"> | 43 | <input type="hidden" name="token" value="{$token}"> |
39 | {if="$http_referer"}<input type="hidden" name="returnurl" value="{$http_referer}">{/if} | 44 | {if="$http_referer"}<input type="hidden" name="returnurl" value="{$http_referer}">{/if} |
40 | </form> | 45 | </form> |
41 | </div> | 46 | </div> |
42 | </div> | 47 | </div> |
43 | {if="$source !== 'firefoxsocialapi'"} | 48 | {if="$source !== 'firefoxsocialapi'"} |
44 | {include="page.footer"} | 49 | {include="page.footer"} |
diff --git a/tpl/feed.atom.html b/tpl/feed.atom.html index 40fd421a..aead0459 100644 --- a/tpl/feed.atom.html +++ b/tpl/feed.atom.html | |||
@@ -30,9 +30,7 @@ | |||
30 | <published>{$value.pub_iso_date}</published> | 30 | <published>{$value.pub_iso_date}</published> |
31 | <updated>{$value.up_iso_date}</updated> | 31 | <updated>{$value.up_iso_date}</updated> |
32 | {/if} | 32 | {/if} |
33 | <content type="html" xml:lang="{$language}"> | 33 | <content type="html" xml:lang="{$language}"><![CDATA[{$value.description}]]></content> |
34 | <![CDATA[{$value.description}]]> | ||
35 | </content> | ||
36 | {loop="$value.taglist"} | 34 | {loop="$value.taglist"} |
37 | <category scheme="{$index_url}?searchtags=" term="{$value|strtolower}" label="{$value}" /> | 35 | <category scheme="{$index_url}?searchtags=" term="{$value|strtolower}" label="{$value}" /> |
38 | {/loop} | 36 | {/loop} |
diff --git a/tpl/includes.html b/tpl/includes.html index f94ce1be..7b2997ce 100644 --- a/tpl/includes.html +++ b/tpl/includes.html | |||
@@ -2,6 +2,7 @@ | |||
2 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | 2 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
3 | <meta name="format-detection" content="telephone=no" /> | 3 | <meta name="format-detection" content="telephone=no" /> |
4 | <meta name="viewport" content="width=device-width,initial-scale=1.0" /> | 4 | <meta name="viewport" content="width=device-width,initial-scale=1.0" /> |
5 | <meta name="referrer" content="same-origin"> | ||
5 | <link rel="alternate" type="application/rss+xml" href="{$feedurl}?do=rss{$searchcrits}#" title="RSS Feed" /> | 6 | <link rel="alternate" type="application/rss+xml" href="{$feedurl}?do=rss{$searchcrits}#" title="RSS Feed" /> |
6 | <link rel="alternate" type="application/atom+xml" href="{$feedurl}?do=atom{$searchcrits}#" title="ATOM Feed" /> | 7 | <link rel="alternate" type="application/atom+xml" href="{$feedurl}?do=atom{$searchcrits}#" title="ATOM Feed" /> |
7 | <link href="images/favicon.ico#" rel="shortcut icon" type="image/x-icon" /> | 8 | <link href="images/favicon.ico#" rel="shortcut icon" type="image/x-icon" /> |
@@ -11,4 +12,4 @@ | |||
11 | {loop="$plugins_includes.css_files"} | 12 | {loop="$plugins_includes.css_files"} |
12 | <link type="text/css" rel="stylesheet" href="{$value}#"/> | 13 | <link type="text/css" rel="stylesheet" href="{$value}#"/> |
13 | {/loop} | 14 | {/loop} |
14 | <link rel="search" type="application/opensearchdescription+xml" href="?do=opensearch#" title="Shaarli search - {$shaarlititle|htmlspecialchars}"/> \ No newline at end of file | 15 | <link rel="search" type="application/opensearchdescription+xml" href="?do=opensearch#" title="Shaarli search - {$shaarlititle|htmlspecialchars}"/> |
diff --git a/tpl/linklist.html b/tpl/linklist.html index 70c9cf79..0f1a5e8c 100644 --- a/tpl/linklist.html +++ b/tpl/linklist.html | |||
@@ -29,10 +29,8 @@ | |||
29 | </form> | 29 | </form> |
30 | {loop="$plugins_header.fields_toolbar"} | 30 | {loop="$plugins_header.fields_toolbar"} |
31 | <form | 31 | <form |
32 | {loop="$value"} | 32 | {loop="$value.attr"} |
33 | {if="$key!='inputs'"} | 33 | {$key}="{$value}" |
34 | {$key}="{$value}" | ||
35 | {/if} | ||
36 | {/loop}> | 34 | {/loop}> |
37 | {loop="$value.inputs"} | 35 | {loop="$value.inputs"} |
38 | <input | 36 | <input |
@@ -83,12 +81,13 @@ | |||
83 | {if="isLoggedIn()"} | 81 | {if="isLoggedIn()"} |
84 | <div class="linkeditbuttons"> | 82 | <div class="linkeditbuttons"> |
85 | <form method="GET" class="buttoneditform"> | 83 | <form method="GET" class="buttoneditform"> |
86 | <input type="hidden" name="edit_link" value="{$value.linkdate}"> | 84 | <input type="hidden" name="edit_link" value="{$value.id}"> |
87 | <input type="image" alt="Edit" src="images/edit_icon.png#" title="Edit" class="button_edit"> | 85 | <input type="image" alt="Edit" src="images/edit_icon.png#" title="Edit" class="button_edit"> |
88 | </form><br> | 86 | </form><br> |
89 | <form method="GET" class="buttoneditform"> | 87 | <form method="POST" class="buttoneditform"> |
88 | <input type="hidden" name="lf_linkdate" value="{$value.id}"> | ||
90 | <input type="hidden" name="token" value="{$token}"> | 89 | <input type="hidden" name="token" value="{$token}"> |
91 | <input type="hidden" name="delete_link" value="{$value.linkdate}"> | 90 | <input type="hidden" name="delete_link"> |
92 | <input type="image" alt="Delete" src="images/delete_icon.png#" title="Delete" | 91 | <input type="image" alt="Delete" src="images/delete_icon.png#" title="Delete" |
93 | class="button_delete" onClick="return confirmDeleteLink();"> | 92 | class="button_delete" onClick="return confirmDeleteLink();"> |
94 | </form> | 93 | </form> |
@@ -102,7 +101,7 @@ | |||
102 | {if="!$hide_timestamps || isLoggedIn()"} | 101 | {if="!$hide_timestamps || isLoggedIn()"} |
103 | {$updated=$value.updated_timestamp ? 'Edited: '. strftime('%c', $value.updated_timestamp) : 'Permalink'} | 102 | {$updated=$value.updated_timestamp ? 'Edited: '. strftime('%c', $value.updated_timestamp) : 'Permalink'} |
104 | <span class="linkdate" title="Permalink"> | 103 | <span class="linkdate" title="Permalink"> |
105 | <a href="?{$value.linkdate|smallHash}"> | 104 | <a href="?{$value.shorturl}"> |
106 | <span title="{$updated}"> | 105 | <span title="{$updated}"> |
107 | {function="strftime('%c', $value.timestamp)"} | 106 | {function="strftime('%c', $value.timestamp)"} |
108 | {if="$value.updated_timestamp"}*{/if} | 107 | {if="$value.updated_timestamp"}*{/if} |
diff --git a/tpl/linklist.paging.html b/tpl/linklist.paging.html index 42f58d0d..86019c01 100644 --- a/tpl/linklist.paging.html +++ b/tpl/linklist.paging.html | |||
@@ -15,10 +15,8 @@ | |||
15 | {loop="$action_plugin"} | 15 | {loop="$action_plugin"} |
16 | <div class="paging_privatelinks"> | 16 | <div class="paging_privatelinks"> |
17 | <a | 17 | <a |
18 | {loop="$value"} | 18 | {loop="$value.attr"} |
19 | {if="$key!='html'"} | 19 | {$key}="{$value}" |
20 | {$key}="{$value}" | ||
21 | {/if} | ||
22 | {/loop}> | 20 | {/loop}> |
23 | {$value.html} | 21 | {$value.html} |
24 | </a> | 22 | </a> |
diff --git a/tpl/loginform.html b/tpl/loginform.html index a49b42d3..84176385 100644 --- a/tpl/loginform.html +++ b/tpl/loginform.html | |||
@@ -2,7 +2,7 @@ | |||
2 | <html> | 2 | <html> |
3 | <head>{include="includes"}</head> | 3 | <head>{include="includes"}</head> |
4 | <body | 4 | <body |
5 | {if="ban_canLogin()"} | 5 | {if="ban_canLogin($conf)"} |
6 | {if="empty($username)"} | 6 | {if="empty($username)"} |
7 | onload="document.loginform.login.focus();" | 7 | onload="document.loginform.login.focus();" |
8 | {else} | 8 | {else} |
@@ -13,7 +13,7 @@ | |||
13 | {include="page.header"} | 13 | {include="page.header"} |
14 | 14 | ||
15 | <div id="headerform"> | 15 | <div id="headerform"> |
16 | {if="!ban_canLogin()"} | 16 | {if="!ban_canLogin($conf)"} |
17 | You have been banned from login after too many failed attempts. Try later. | 17 | You have been banned from login after too many failed attempts. Try later. |
18 | {else} | 18 | {else} |
19 | <form method="post" name="loginform"> | 19 | <form method="post" name="loginform"> |
diff --git a/tpl/page.header.html b/tpl/page.header.html index 89879678..cce61ec4 100644 --- a/tpl/page.header.html +++ b/tpl/page.header.html | |||
@@ -36,10 +36,8 @@ | |||
36 | <li><a href="?do=daily">Daily</a></li> | 36 | <li><a href="?do=daily">Daily</a></li> |
37 | {loop="$plugins_header.buttons_toolbar"} | 37 | {loop="$plugins_header.buttons_toolbar"} |
38 | <li><a | 38 | <li><a |
39 | {loop="$value"} | 39 | {loop="$value.attr"} |
40 | {if="$key!='html'"} | 40 | {$key}="{$value}" |
41 | {$key}="{$value}" | ||
42 | {/if} | ||
43 | {/loop}> | 41 | {/loop}> |
44 | {$value.html} | 42 | {$value.html} |
45 | </a></li> | 43 | </a></li> |
diff --git a/tpl/pluginsadmin.html b/tpl/pluginsadmin.html index 672f4993..ead1734e 100644 --- a/tpl/pluginsadmin.html +++ b/tpl/pluginsadmin.html | |||
@@ -38,11 +38,11 @@ | |||
38 | <tr data-line="{$key}" data-order="{$counter}"> | 38 | <tr data-line="{$key}" data-order="{$counter}"> |
39 | <td class="center"><input type="checkbox" name="{$key}" id="{$key}" checked="checked"></td> | 39 | <td class="center"><input type="checkbox" name="{$key}" id="{$key}" checked="checked"></td> |
40 | <td class="center"> | 40 | <td class="center"> |
41 | <a href="#" | 41 | <a href="#" class="arrow" |
42 | onclick="return orderUp(this.parentNode.parentNode.getAttribute('data-order'));"> | 42 | onclick="return orderUp(this.parentNode.parentNode.getAttribute('data-order'));"> |
43 | ▲ | 43 | ▲ |
44 | </a> | 44 | </a> |
45 | <a href="#" | 45 | <a href="#" class="arrow" |
46 | onclick="return orderDown(this.parentNode.parentNode.getAttribute('data-order'));"> | 46 | onclick="return orderDown(this.parentNode.parentNode.getAttribute('data-order'));"> |
47 | ▼ | 47 | ▼ |
48 | </a> | 48 | </a> |
diff --git a/tpl/tools.html b/tpl/tools.html index 8e285f44..e06d239d 100644 --- a/tpl/tools.html +++ b/tpl/tools.html | |||
@@ -50,12 +50,15 @@ | |||
50 | Then click "✚Add Note" button anytime to start composing a private Note (text post) to your Shaarli. | 50 | Then click "✚Add Note" button anytime to start composing a private Note (text post) to your Shaarli. |
51 | </span> | 51 | </span> |
52 | </a><br><br> | 52 | </a><br><br> |
53 | |||
54 | {if="$sslenabled"} | ||
53 | <a class="smallbutton" onclick="activateFirefoxSocial(this)"> | 55 | <a class="smallbutton" onclick="activateFirefoxSocial(this)"> |
54 | <b>✚Add to Firefox social</b> | 56 | <b>✚Add to Firefox social</b> |
55 | </a> | 57 | </a> |
56 | <a href="#"> | 58 | <a href="#"> |
57 | <span>⇐ Click on this button to add Shaarli to the "Share this page" button in Firefox.</span> | 59 | <span>⇐ Click on this button to add Shaarli to the "Share this page" button in Firefox.</span> |
58 | </a><br><br> | 60 | </a><br><br> |
61 | {/if} | ||
59 | 62 | ||
60 | {loop="$tools_plugin"} | 63 | {loop="$tools_plugin"} |
61 | {$value} | 64 | {$value} |
@@ -64,6 +67,7 @@ | |||
64 | <div class="clear"></div> | 67 | <div class="clear"></div> |
65 | 68 | ||
66 | <script> | 69 | <script> |
70 | {if="$sslenabled"} | ||
67 | function activateFirefoxSocial(node) { | 71 | function activateFirefoxSocial(node) { |
68 | var loc = location.href; | 72 | var loc = location.href; |
69 | var baseURL = loc.substring(0, loc.lastIndexOf("/")); | 73 | var baseURL = loc.substring(0, loc.lastIndexOf("/")); |
@@ -87,7 +91,7 @@ | |||
87 | var activate = new CustomEvent("ActivateSocialFeature"); | 91 | var activate = new CustomEvent("ActivateSocialFeature"); |
88 | node.dispatchEvent(activate); | 92 | node.dispatchEvent(activate); |
89 | } | 93 | } |
90 | 94 | {/if} | |
91 | function alertBookmarklet() { | 95 | function alertBookmarklet() { |
92 | alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...'); | 96 | alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...'); |
93 | return false; | 97 | return false; |